wfe.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <?php
  2. /*
  3. * __
  4. * __ __/ _| ___
  5. * \ \ /\ / / |_ / _ \
  6. * \ V V /| _| __/
  7. * \_/\_/ |_| \___|
  8. *
  9. * Web File Explorer
  10. * This is free software!
  11. *
  12. * TODO
  13. * - Sorting
  14. * - Upload
  15. * - Download of gzipped folders
  16. * - REST API (?)
  17. * - Mobile version
  18. */
  19. # Debug
  20. ini_set('display_errors', 1);
  21. ini_set('display_startup_errors', 1);
  22. error_reporting(E_ALL);
  23. # Configuration
  24. // Code version
  25. $version = "1.1.0";
  26. // Set to 'false' to disable security
  27. $jail = false;
  28. # Constants
  29. // Folder in which the script runs
  30. define("FOLDER", getcwd());
  31. # Main variables (warning! Editing below this line is potentially dangerous!)
  32. // The script itself
  33. $url = $_SERVER["PHP_SELF"];
  34. $script = basename($url);
  35. // Working directory
  36. $path = getcwd();
  37. // Download file
  38. $download = null;
  39. // Open directory
  40. $directory = null;
  41. // Place for 'home' and 'parent directory' items
  42. $default = array();
  43. // Place for directories
  44. $directories = array();
  45. // Place for files
  46. $files = array();
  47. # Security stuff
  48. // Sanitize and deobfuscate qs parameters
  49. if (isset($_GET["f"])) {
  50. $download = filter_var($_GET["f"], FILTER_SANITIZE_STRING);
  51. $download = deobfuscate($download);
  52. }
  53. if (isset($_GET["d"])) {
  54. $directory = filter_var($_GET["d"], FILTER_SANITIZE_STRING);
  55. $directory = deobfuscate($directory);
  56. }
  57. // Canonicalized absolute pathname
  58. // Does not follow symbolic links
  59. $path = realpath($path) . "/";
  60. if ($directory !== null) {
  61. $path = realpath($directory) . "/";
  62. }
  63. // Document root directory
  64. $root = substr($path, 0, strlen(FOLDER));
  65. if ($download !== null) {
  66. $root = substr($download, 0, strlen(FOLDER));
  67. }
  68. // Do not access the entire file system!
  69. if ($jail === true && $root !== FOLDER) {
  70. header("Location: $url");
  71. exit;
  72. }
  73. # Main part
  74. // Download file
  75. if ($download !== null) {
  76. // See https://code-boxx.com/php-read-file/
  77. // Start output buffer
  78. ob_start();
  79. // HTTP headers to force download
  80. header("Content-Type: application/octet-stream");
  81. header("Content-Disposition: attachment; filename=\"" . basename($download) . "\"");
  82. header("Expires: 0");
  83. header("Cache-Control: must-revalidate");
  84. header("Pragma: public");
  85. header("Content-Length: " . filesize($download));
  86. // Output all the headers and stop buffering
  87. ob_end_flush();
  88. // Read and output file directly
  89. readfile($download);
  90. exit();
  91. }
  92. // Open directory
  93. // See https://www.php.net/manual/en/function.readdir.php
  94. if ($handle = opendir($path)) {
  95. // Get each entry
  96. while (false !== ($item = readdir($handle))) {
  97. $items[] = $item;
  98. }
  99. // Close directory
  100. closedir($handle);
  101. } else {
  102. // Unreadable data, or something worse
  103. header("Location: $url");
  104. exit;
  105. }
  106. // Loops through the array of items
  107. for ($i = 0; $i < count($items); $i++) {
  108. // Get item name
  109. $name = $items[$i];
  110. if ($name !== "." && $name !== "..") {
  111. // Directory
  112. if (is_dir($path . $name)) {
  113. if (is_readable($path . $name)) {
  114. $link = "?d=" . obfuscate($path . $name);
  115. } else {
  116. $link = null;
  117. }
  118. $directories[$i]["link"] = $link;
  119. $directories[$i]["name"] = "<b>&#8600;</b> " . $name;
  120. $directories[$i]["type"] = "Directory";
  121. $directories[$i]["size"] = 0;
  122. $directories[$i]["date"] = prettydate($path . $items[$i]);
  123. }
  124. // File
  125. if (is_file($path . $name) && $name !== $script) {
  126. if (is_readable($path . $name)) {
  127. $link = "?f=" . obfuscate($path . $name);
  128. } else {
  129. $link = null;
  130. }
  131. $files[$i]["link"] = $link;
  132. $files[$i]["name"] = "<b>&#9733;</b> " . $name;
  133. $files[$i]["type"] = prettytype($path . $items[$i]);
  134. $files[$i]["size"] = prettysize(filesize($path . $items[$i]));
  135. $files[$i]["date"] = prettydate($path . $items[$i]);
  136. }
  137. }
  138. }
  139. // Home directory
  140. $default[0]["link"] = $url;
  141. $default[0]["name"] = "<b>&#9873;</b> Home";
  142. $default[0]["type"] = "Directory";
  143. $default[0]["size"] = 0;
  144. $default[0]["date"] = prettydate(__DIR__);
  145. // Parent directory
  146. $default[1]["link"] = $url . "?d=" . obfuscate($path . "../");
  147. $default[1]["name"] = "<b>&#8598;</b> Parent directory";
  148. $default[1]["type"] = "Directory";
  149. $default[1]["size"] = 0;
  150. $default[1]["date"] = prettydate($path . "..");
  151. // Sort arrays
  152. $n = array_column($directories, "name");
  153. $d = array_column($directories, "date");
  154. array_multisort($n, SORT_ASC, $d, SORT_ASC, $directories);
  155. $n = array_column($files, "name");
  156. $d = array_column($files, "date");
  157. array_multisort($n, SORT_ASC, $d, SORT_ASC, $files);
  158. # Output
  159. // CSS3 code
  160. $css3 = <<<CSS3
  161. body{background-color:DarkSlateGrey;color:FloralWhite;font:12pt "Lucida Console","Courier New",Monospace;margin:15pt}
  162. a{text-decoration:none}
  163. a:link{color:Chartreuse}
  164. a:visited{color:Chartreuse}
  165. b{color:BlanchedAlmond;font:lighter 14pt Sans-serif}
  166. h1{background-color:LightSlateGray;border-radius:15pt 15pt 0 0;color:DarkSlateGrey;font:35pt Sans-serif;margin-bottom:-12pt;padding:4pt 0 16pt;text-align:center}
  167. footer{font:11pt Sans-serif;margin-top:10pt;text-align:center}
  168. footer>p{margin:0;padding-top:1pt}
  169. .container{background-color:DarkSlateGrey;border:3pt solid LightSlateGray;border-radius:15pt;padding:20pt 0 24pt 28pt}
  170. .flex-container{display:flex}
  171. .flex-container>div{height:16pt;line-height:16pt;overflow:hidden}
  172. CSS3;
  173. // HTML5 code
  174. // Useful link for SVG encoding: https://yoksel.github.io/url-encoder/
  175. $html5 = <<<HTML5
  176. <html>
  177. <head>
  178. <meta charset="UTF-8">
  179. <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cpath d='m 244.04381,212.92307 c -3.60647,5.42304 -11.7731,7.4057 -16.66459,12.15712 -5.85789,5.69014 -15.07376,14.52505 -27.53351,14.56409 -11.90529,0.039 -23.17775,-6.80379 -29.62542,-14.81268 -3.5757,-4.4415 -13.97271,-7.23609 -16.88903,-11.90853 -4.02131,-6.44284 -7.43438,-17.16455 -6.69618,-23.10071 0.3092,-2.48641 8.56734,-4.9239 9.8384,-2.16705 9.95411,21.58982 24.54537,25.35385 43.17247,25.35385 18.07331,0 30.55033,0.36987 41.15361,-25.02313 1.83852,-4.40292 9.05882,-0.9737 9.90873,1.80993 3.03466,9.93904 0.0296,0.007 0.0592,0.0132 0.0296,0.007 -2.51434,16.78435 -6.72367,23.11391 z m -43.81767,73.05112 c 23.78344,-9.9385 60.87929,-24.98051 85.68809,-29.20578 7.16001,-15.01208 44.20627,-46.00162 37.17641,0.62229 21.01689,14.49958 12.23597,39.78412 -26.97271,17.55322 -19.72442,1.44079 -55.38285,15.82106 -72.01094,24.1409 15.66034,7.19574 47.25562,25.91831 60.92706,33.77736 81.63685,7.3009 45.27116,32.11955 25.03291,34.52364 0.4083,10.6284 -33.73609,31.50279 -42.96578,-15.46842 -9.12497,-9.18539 -57.18179,-38.25033 -69.02042,-40.58877 -16.25551,6.11637 -53.97909,29.77844 -69.23713,42.92918 -5.11212,33.40551 -22.4687,30.42472 -39.040204,11.62746 -32.625926,-0.41169 -43.92465,-34.42716 25.842904,-35.66 13.44857,-6.89885 44.79673,-24.88779 59.07368,-31.14045 -24.9652,-10.1759 -54.35502,-19.69505 -74.10937,-24.01334 -29.075956,20.96464 -49.208492,2.25973 -26.765508,-17.36886 -2.332332,-9.71105 2.286775,-40.31952 44.170698,-3.5783 16.25235,5.43691 62.57246,23.11498 82.21031,31.84987 z m -0.67179,-140.77131 c -3.60112,6.37202 -16.27424,16.61667 -16.30496,20.33965 -0.0815,9.87553 9.74958,10.02745 16.48059,5.67642 6.89338,3.69555 16.41887,5.27089 15.9851,-5.92036 -0.16512,-4.25985 -11.39051,-12.31985 -16.16073,-20.09571 z m 33.3075,39.26688 c -3.49053,2.19568 -1.68005,10.35041 -5.48263,11.8104 -16.98182,6.52006 -38.34712,5.08379 -55.04437,0.68035 -5.27027,-1.3899 -4.64177,-11.44672 -6.60809,-12.49075 -11.77695,-6.25305 -29.10242,-7.82873 -38.35672,-15.60274 -4.22536,-3.54949 -1.70953,-27.39908 -9.24305,-31.90879 -6.89071,-4.12493 -9.65016,-19.05556 -12.07601,-31.80112 -2.41322,-12.679195 -4.25851,-38.148675 10.32399,-55.221424 14.5825,-17.07275 41.90039,-31.233748 83.70406,-31.233748 41.77609,0 69.79438,13.357592 83.60205,31.233748 13.85491,17.937329 10.06316,43.348359 7.91079,55.221424 -2.35911,13.01347 -8.68154,26.53614 -14.22102,31.44531 -5.76236,5.10669 -3.96697,29.57305 -6.41233,32.2646 -4.79997,5.2832 -27.06077,8.6607 -38.09667,15.60274 z m -97.47263,-59.74818 c -0.35747,17.81781 17.49732,26.00449 32.18065,21.01428 16.84431,-5.72462 19.00749,-35.9277 0.85722,-41.21401 -14.88584,-4.33553 -32.66291,1.51015 -33.03787,20.19973 z M 244.023,146.74927 c 19.35737,-2.42285 25.18053,-33.03141 8.40887,-41.18179 -13.50259,-6.561735 -34.33633,-1.33569 -36.23743,17.00112 -2.28851,22.07337 13.66571,25.95336 27.82856,24.18067 z' /%3E%3C/svg%3E" type="image/svg+xml" />
  180. <title>$script</title>
  181. <style>$css3</style>
  182. </head>
  183. <body>
  184. <h1>$script</h1>
  185. <div class="container">
  186. <div class="flex-container" style="margin-bottom: 8pt">
  187. <div style="flex-basis: 50%; font-weight: bolder; margin-left: 15pt">Name</a></div>
  188. <div style="flex-basis: 20%; font-weight: bolder">Type</div>
  189. <div style="flex-basis: 10%; font-weight: bolder">Size</div>
  190. <div style="flex-basis: 20%; font-weight: bolder">Date</div>
  191. </div>
  192. HTML5;
  193. echo $html5;
  194. foreach ($default as $value) {
  195. echo '<div class="flex-container">';
  196. echo '<div style="flex-basis: 50%"><a href=' . $value["link"] . '>' . $value["name"] . '</a></div>';
  197. echo '<div style="flex-basis: 20%; margin-left: 15pt">' . $value["type"] . '</div>';
  198. echo '<div style="flex-basis: 10%">' . $value["size"] . '</div>';
  199. echo '<div style="flex-basis: 20%">' . $value["date"] . '</div>';
  200. echo '</div>';
  201. }
  202. foreach ($directories as $value) {
  203. echo '<div class="flex-container">';
  204. if ($value["link"] === null) {
  205. echo '<div style="flex-basis: 50%; color: Pink">' . $value["name"] . '</div>';
  206. } else {
  207. echo '<div style="flex-basis: 50%"><a href=' . $value["link"] . '>' . $value["name"] . '</a></div>';
  208. }
  209. echo '<div style="flex-basis: 20%; margin-left: 15pt">' . $value["type"] . '</div>';
  210. echo '<div style="flex-basis: 10%">' . $value["size"] . '</div>';
  211. echo '<div style="flex-basis: 20%">' . $value["date"] . '</div>';
  212. echo '</div>';
  213. }
  214. foreach ($files as $value) {
  215. echo '<div class="flex-container">';
  216. if ($value["link"] === null) {
  217. echo '<div style="flex-basis: 50%; color: Pink">' . $value["name"] . '</div>';
  218. } else {
  219. echo '<div style="flex-basis: 50%"><a href=' . $value["link"] . '>' . $value["name"] . '</a></div>';
  220. }
  221. echo '<div style="flex-basis: 20%; margin-left: 13pt">' . $value["type"] . '</div>';
  222. echo '<div style="flex-basis: 10%">' . $value["size"] . '</div>';
  223. echo '<div style="flex-basis: 20%">' . $value["date"] . '</div>';
  224. echo '</div>';
  225. }
  226. $html5 = <<<HTML5
  227. </div>
  228. <footer>
  229. <p>Current path: <b>$path</b></p>
  230. <p>$script &bull; Web File Explorer &bull; v$version</p>
  231. </footer>
  232. </body>
  233. </html>
  234. HTML5;
  235. echo $html5;
  236. # Functions
  237. // String obfuscation
  238. // ROT-13 + Zlib compression + base64 encoding
  239. // See: https://stackoverflow.com/questions/2996049/how-to-compress-decompress-a-long-query-string-in-php
  240. function obfuscate($str)
  241. {
  242. return rtrim(strtr(base64_encode(gzdeflate(str_rot13($str), 9)), '+/', '-_'), '=');
  243. }
  244. function deobfuscate($str)
  245. {
  246. return str_rot13(gzinflate(base64_decode(strtr($str, '-_', '+/'))));
  247. }
  248. // Prettifies file size
  249. function prettysize($size)
  250. {
  251. if ($size < 1024) {
  252. $size = $size;
  253. } elseif (($size < 1048576) && ($size > 1023)) {
  254. $size = round($size / 1024, 1) . "K";
  255. } elseif (($size < 1073741824) && ($size > 1048575)) {
  256. $size = round($size / 1048576, 1) . "M";
  257. } else {
  258. $size = round($size / 1073741824, 1) . "G";
  259. }
  260. return $size;
  261. }
  262. // Prettifies file type
  263. function prettytype($file)
  264. {
  265. // Gets file extension
  266. $extn = pathinfo($file, PATHINFO_EXTENSION);
  267. switch ($extn) {
  268. case "png":
  269. $type = "PNG image";
  270. break;
  271. case "jpg":
  272. $type = "JPEG image";
  273. break;
  274. case "jpeg":
  275. $type = "JPEG image";
  276. break;
  277. case "svg":
  278. $type = "SVG image";
  279. break;
  280. case "gif":
  281. $type = "GIF image";
  282. break;
  283. case "ico":
  284. $type = "Windows icon";
  285. break;
  286. case "txt":
  287. $type = "Text file";
  288. break;
  289. case "log":
  290. $type = "Log file";
  291. break;
  292. case "htm":
  293. $type = "HTML file";
  294. break;
  295. case "html":
  296. $type = "HTML file";
  297. break;
  298. case "xhtml":
  299. $type = "HTML file";
  300. break;
  301. case "shtml":
  302. $type = "HTML file";
  303. break;
  304. case "php":
  305. $type = "PHP script";
  306. break;
  307. case "js":
  308. $type = "Javascript file";
  309. break;
  310. case "css":
  311. $type = "Stylesheet";
  312. break;
  313. case "pdf":
  314. $type = "PDF document";
  315. break;
  316. case "xls":
  317. $type = "Spreadsheet";
  318. break;
  319. case "xlsx":
  320. $type = "Spreadsheet";
  321. break;
  322. case "doc":
  323. $type = "Microsoft Word document";
  324. break;
  325. case "docx":
  326. $type = "Microsoft Word document";
  327. break;
  328. case "zip":
  329. $type = "ZIP archive";
  330. break;
  331. case "htaccess":
  332. $type = "Apache config file";
  333. break;
  334. case "exe":
  335. $type = "Windows executable";
  336. break;
  337. case "torrent":
  338. $type = "BitTorrent file";
  339. break;
  340. default:
  341. if ($extn !== "") {
  342. $type = strtoupper($extn) . " file";
  343. } else {
  344. $type = "Unknown";
  345. }
  346. break;
  347. }
  348. return $type;
  349. }
  350. // Prettifies modification date
  351. function prettydate($file)
  352. {
  353. return date("Y-m-d H:i:s", filemtime($file));
  354. }