wfe.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  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. // Favicon
  160. $png = <<<PNG
  161. iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABHNCSV
  162. QICAgIfAhkiAAAAAlwSFlzAAABGwAAARsBjfdO5QAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYX
  163. BlLm9yZ5vuPBoAAAJMSURBVEiJxdZfaI9RGAfwj22IjGEthiimxJ3SSk2SXCk3lOLChQs3qymS3Y
  164. ioFbmZGxcrV4ikkCTjRrlxofn/J4Qp/zMt82dzcc7b791se3+/31751uk973Oe5/mec55znufwnz
  165. CuRP0pWIMlqIv27/EQnfiS6+xQi2P4joER2k90YHZepPPxbBTCoe0FFuRBfLEE0qSdHCtpNX6VQf
  166. w5y3FFxng9KsuY8GQZBzeL+GuqP4A7eDyMXlccS/Ah6o8Jb6OT1pTsjMK2Hk7Jj0bZ2SynWSuGq/
  167. F7LSXrSvUvpfpP4vd4EX4zUYdPOIUazMF9/EZ/JJkU9e7gQh6kCS75++TuxP5h5HvyJG4WVpc4fy
  168. qc3Bq8S8n7sDZPYmjAPrzy9yofoQUz8iZNcBdbIlGfwuq3yjm2Q3ERt7EK04WtbsQ9Ocd2KGpxzu
  169. Bt7kc7JuZBUIkVmDDCeIsQ6yfYOILONOwW8n3R2C6s5jlWlmIYUadQSleXYnja4AJ/HutRNYpNBZ
  170. pwQEgwiX3rKDaDME8hdp14nXLyBoewKKXfgDZ0R51vuI6bCne76ArXiMWp/2XYiwepSd0Q8ne/kD
  171. 4vY4OQPhMsxtJiSbOwKxIlu/ADm/JyPhyW40ok68UR4XHXFydyQgjTmFElxK9JqL/9sXVgbkpvoc
  172. Ld7sVBTC2HsEJ4yvYYnCi6sW4Uu834GHV/46WQ6bYo8v0+K848TXpLyFpZqFd49iStBzOLISZcl2
  173. bhtTggFIFi0Z4ibVNm3BuwA+NLsKkWyue2cgj/Of4AiebN88P8fu4AAAAASUVORK5CYII=
  174. PNG;
  175. // CSS3 code
  176. $css3 = <<<CSS3
  177. body{background-color:DarkSlateGrey;color:FloralWhite;font:12pt "Lucida Console","Courier New",Monospace;margin:15pt}
  178. a{text-decoration:none;color:Chartreuse}
  179. b{color:BlanchedAlmond;font:lighter 14pt Sans-serif}
  180. h1{background-color:LightSlateGray;border-radius:15pt 15pt 0 0;margin-bottom:-12pt;padding:4pt 0 16pt;text-align:center}
  181. h1>span{color:DarkSlateGrey;font:35pt Sans-serif}
  182. footer{font:11pt Sans-serif;margin-top:10pt;text-align:center}
  183. footer>p{margin:0;padding-top:1pt}
  184. .container{background-color:DarkSlateGrey;border:3pt solid LightSlateGray;border-radius:15pt;padding:20pt 0 24pt 28pt}
  185. .flex-container{display:flex}
  186. .flex-container>div{height:16pt;line-height:16pt;overflow:hidden}
  187. CSS3;
  188. // HTML5 code
  189. // Useful link for SVG encoding: https://yoksel.github.io/url-encoder/
  190. $html5 = <<<HTML5
  191. <!DOCTYPE html>
  192. <html lang="en">
  193. <head>
  194. <meta charset="UTF-8">
  195. <link rel="icon" href="data:image/png;base64,$png">
  196. <title>$script</title>
  197. <style>$css3</style>
  198. </head>
  199. <body>
  200. <h1><span>$script</span></h1>
  201. <div class="container">
  202. <div class="flex-container" style="margin-bottom: 8pt">
  203. <div style="flex-basis: 50%; font-weight: bolder; margin-left: 15pt">Name</div>
  204. <div style="flex-basis: 20%; font-weight: bolder">Type</div>
  205. <div style="flex-basis: 10%; font-weight: bolder">Size</div>
  206. <div style="flex-basis: 20%; font-weight: bolder">Date</div>
  207. </div>
  208. HTML5;
  209. echo $html5;
  210. foreach ($default as $value) {
  211. echo '<div class="flex-container">';
  212. echo '<div style="flex-basis: 50%"><a href="' . $value["link"] . '">' . $value["name"] . '</a></div>';
  213. echo '<div style="flex-basis: 20%; margin-left: 15pt">' . $value["type"] . '</div>';
  214. echo '<div style="flex-basis: 10%">' . $value["size"] . '</div>';
  215. echo '<div style="flex-basis: 20%">' . $value["date"] . '</div>';
  216. echo '</div>';
  217. }
  218. foreach ($directories as $value) {
  219. echo '<div class="flex-container">';
  220. if ($value["link"] === null) {
  221. echo '<div style="flex-basis: 50%; color: Pink">' . $value["name"] . '</div>';
  222. } else {
  223. echo '<div style="flex-basis: 50%"><a href="' . $value["link"] . '">' . $value["name"] . '</a></div>';
  224. }
  225. echo '<div style="flex-basis: 20%; margin-left: 15pt">' . $value["type"] . '</div>';
  226. echo '<div style="flex-basis: 10%">' . $value["size"] . '</div>';
  227. echo '<div style="flex-basis: 20%">' . $value["date"] . '</div>';
  228. echo '</div>';
  229. }
  230. foreach ($files as $value) {
  231. echo '<div class="flex-container">';
  232. if ($value["link"] === null) {
  233. echo '<div style="flex-basis: 50%; color: Pink">' . $value["name"] . '</div>';
  234. } else {
  235. echo '<div style="flex-basis: 50%"><a href="' . $value["link"] . '">' . $value["name"] . '</a></div>';
  236. }
  237. echo '<div style="flex-basis: 20%; margin-left: 13pt">' . $value["type"] . '</div>';
  238. echo '<div style="flex-basis: 10%">' . $value["size"] . '</div>';
  239. echo '<div style="flex-basis: 20%">' . $value["date"] . '</div>';
  240. echo '</div>';
  241. }
  242. $html5 = <<<HTML5
  243. </div>
  244. <footer>
  245. <p>Current path: <b>$path</b></p>
  246. <p>$script &bull; Web File Explorer &bull; v$version</p>
  247. </footer>
  248. </body>
  249. </html>
  250. HTML5;
  251. echo $html5;
  252. # Functions
  253. // String obfuscation
  254. // ROT-13 + Zlib compression + base64 encoding
  255. // See: https://stackoverflow.com/questions/2996049/how-to-compress-decompress-a-long-query-string-in-php
  256. function obfuscate($str)
  257. {
  258. return rtrim(strtr(base64_encode(gzdeflate(str_rot13($str), 9)), '+/', '-_'), '=');
  259. }
  260. function deobfuscate($str)
  261. {
  262. return str_rot13(gzinflate(base64_decode(strtr($str, '-_', '+/'))));
  263. }
  264. // Prettifies file size
  265. function prettysize($size)
  266. {
  267. if ($size < 1024) {
  268. $size = $size;
  269. } elseif (($size < 1048576) && ($size > 1023)) {
  270. $size = round($size / 1024, 1) . "K";
  271. } elseif (($size < 1073741824) && ($size > 1048575)) {
  272. $size = round($size / 1048576, 1) . "M";
  273. } else {
  274. $size = round($size / 1073741824, 1) . "G";
  275. }
  276. return $size;
  277. }
  278. // Prettifies file type
  279. function prettytype($file)
  280. {
  281. // Gets file extension
  282. $extn = pathinfo($file, PATHINFO_EXTENSION);
  283. switch ($extn) {
  284. case "png":
  285. $type = "PNG image";
  286. break;
  287. case "jpg":
  288. $type = "JPEG image";
  289. break;
  290. case "jpeg":
  291. $type = "JPEG image";
  292. break;
  293. case "svg":
  294. $type = "SVG image";
  295. break;
  296. case "gif":
  297. $type = "GIF image";
  298. break;
  299. case "ico":
  300. $type = "Windows icon";
  301. break;
  302. case "txt":
  303. $type = "Text file";
  304. break;
  305. case "log":
  306. $type = "Log file";
  307. break;
  308. case "htm":
  309. $type = "HTML file";
  310. break;
  311. case "html":
  312. $type = "HTML file";
  313. break;
  314. case "xhtml":
  315. $type = "HTML file";
  316. break;
  317. case "shtml":
  318. $type = "HTML file";
  319. break;
  320. case "php":
  321. $type = "PHP script";
  322. break;
  323. case "js":
  324. $type = "Javascript file";
  325. break;
  326. case "css":
  327. $type = "Stylesheet";
  328. break;
  329. case "pdf":
  330. $type = "PDF document";
  331. break;
  332. case "xls":
  333. $type = "Spreadsheet";
  334. break;
  335. case "xlsx":
  336. $type = "Spreadsheet";
  337. break;
  338. case "doc":
  339. $type = "Microsoft Word document";
  340. break;
  341. case "docx":
  342. $type = "Microsoft Word document";
  343. break;
  344. case "zip":
  345. $type = "ZIP archive";
  346. break;
  347. case "htaccess":
  348. $type = "Apache config file";
  349. break;
  350. case "exe":
  351. $type = "Windows executable";
  352. break;
  353. case "torrent":
  354. $type = "BitTorrent file";
  355. break;
  356. default:
  357. if ($extn !== "") {
  358. $type = strtoupper($extn) . " file";
  359. } else {
  360. $type = "Unknown";
  361. }
  362. break;
  363. }
  364. return $type;
  365. }
  366. // Prettifies modification date
  367. function prettydate($file)
  368. {
  369. return date("Y-m-d H:i:s", filemtime($file));
  370. }