12393fff5SJames Collins<?php 22393fff5SJames Collins/** 33cffc6f4SJames Collins * Mikio CSS/LESS Engine 42393fff5SJames Collins * 53cffc6f4SJames Collins * @link http://dokuwiki.org/template:mikio 6*12a6b3a2SJames Collins * @license GPLv2 7*12a6b3a2SJames Collins * @author James Collins 82393fff5SJames Collins */ 92393fff5SJames Collins 10*12a6b3a2SJames Collinsrequire(__DIR__ . '/inc/polyfill-ctype.php'); 113cffc6f4SJames Collins 124afa967aSJames Collinsif (!class_exists('lessc')) { 13*12a6b3a2SJames Collins require(__DIR__ . '/inc/stemmechanics/lesserphp/lessc.inc.php'); 144afa967aSJames Collins} 154afa967aSJames Collins 16*12a6b3a2SJames Collinsfunction logInvalidRequest($reason, $input) { 17*12a6b3a2SJames Collins error_log("[css.php] $reason | input: $input | IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'unknown')); 1817dbafb6SJames Collins} 1917dbafb6SJames Collins 202393fff5SJames Collinstry { 21*12a6b3a2SJames Collins if (!isset($_GET['css'])) { 22*12a6b3a2SJames Collins http_response_code(404); 23*12a6b3a2SJames Collins echo "The requested file could not be found"; 24*12a6b3a2SJames Collins exit; 25*12a6b3a2SJames Collins } 26*12a6b3a2SJames Collins 27*12a6b3a2SJames Collins $cssFileList = explode(',', $_GET['css']); 28*12a6b3a2SJames Collins $pluginRoot = realpath(__DIR__ . '/'); 29*12a6b3a2SJames Collins $allowedDirs = [ 30*12a6b3a2SJames Collins realpath($pluginRoot . '/assets'), 31*12a6b3a2SJames Collins realpath($pluginRoot . '/styles') 32*12a6b3a2SJames Collins ]; 33*12a6b3a2SJames Collins $allowedExtensions = ['css', 'less']; 343cffc6f4SJames Collins $css = ''; 35*12a6b3a2SJames Collins $failed = false; 363cffc6f4SJames Collins 37*12a6b3a2SJames Collins foreach ($cssFileList as $rawInput) { 38*12a6b3a2SJames Collins // Strip query/hash 39*12a6b3a2SJames Collins $cleanInput = explode('?', $rawInput, 2)[0]; 40*12a6b3a2SJames Collins $cleanInput = trim(str_replace(['..', '\\'], '', $cleanInput)); 41*12a6b3a2SJames Collins if (empty($cleanInput)) { 423cffc6f4SJames Collins $failed = true; 43*12a6b3a2SJames Collins logInvalidRequest("Empty or invalid path", $rawInput); 44*12a6b3a2SJames Collins continue; 45*12a6b3a2SJames Collins } 46*12a6b3a2SJames Collins 47*12a6b3a2SJames Collins $resolvedPath = realpath($pluginRoot . '/' . $cleanInput); 48*12a6b3a2SJames Collins $ext = pathinfo($resolvedPath, PATHINFO_EXTENSION); 49*12a6b3a2SJames Collins 50*12a6b3a2SJames Collins if ( 51*12a6b3a2SJames Collins !$resolvedPath || 52*12a6b3a2SJames Collins !file_exists($resolvedPath) || 53*12a6b3a2SJames Collins !in_array($ext, $allowedExtensions, true) 54*12a6b3a2SJames Collins ) { 55*12a6b3a2SJames Collins $failed = true; 56*12a6b3a2SJames Collins logInvalidRequest("Invalid file or extension", $rawInput); 57*12a6b3a2SJames Collins continue; 58*12a6b3a2SJames Collins } 59*12a6b3a2SJames Collins 60*12a6b3a2SJames Collins // Confirm file is within allowed directories 61*12a6b3a2SJames Collins $allowed = false; 62*12a6b3a2SJames Collins foreach ($allowedDirs as $dir) { 63*12a6b3a2SJames Collins if (strpos($resolvedPath, $dir) === 0) { 64*12a6b3a2SJames Collins $allowed = true; 65*12a6b3a2SJames Collins break; 66518c05ebSJames Collins } 67518c05ebSJames Collins } 682393fff5SJames Collins 69*12a6b3a2SJames Collins if (!$allowed) { 70*12a6b3a2SJames Collins $failed = true; 71*12a6b3a2SJames Collins logInvalidRequest("File outside allowed directory", $rawInput); 72*12a6b3a2SJames Collins continue; 73*12a6b3a2SJames Collins } 74*12a6b3a2SJames Collins 75*12a6b3a2SJames Collins $css .= file_get_contents($resolvedPath); 76*12a6b3a2SJames Collins } 77*12a6b3a2SJames Collins 78*12a6b3a2SJames Collins if ($failed) { 79*12a6b3a2SJames Collins http_response_code(404); 80*12a6b3a2SJames Collins echo "The requested file could not be found"; 81*12a6b3a2SJames Collins exit; 82*12a6b3a2SJames Collins } 833cffc6f4SJames Collins 842393fff5SJames Collins header('Content-Type: text/css; charset=utf-8'); 852393fff5SJames Collins 862393fff5SJames Collins $less = new lessc(); 872393fff5SJames Collins $less->setPreserveComments(false); 882393fff5SJames Collins 89*12a6b3a2SJames Collins // Optional variables (future-proofed) 90*12a6b3a2SJames Collins $rawVars = []; 91*12a6b3a2SJames Collins $vars = []; 923cffc6f4SJames Collins if (isset($rawVars['replacements'])) { 933cffc6f4SJames Collins foreach ($rawVars['replacements'] as $key => $val) { 94*12a6b3a2SJames Collins if (strpos($key, '__') === 0 && substr($key, -2) === '__') { 953cffc6f4SJames Collins $vars['ini_' . substr($key, 2, -2)] = $val; 963cffc6f4SJames Collins } 973cffc6f4SJames Collins } 983cffc6f4SJames Collins } 993cffc6f4SJames Collins 100*12a6b3a2SJames Collins if (!empty($vars)) { 1013cffc6f4SJames Collins $less->setVariables($vars); 1023cffc6f4SJames Collins } 1033cffc6f4SJames Collins 104*12a6b3a2SJames Collins echo $less->compile($css); 105*12a6b3a2SJames Collins 106*12a6b3a2SJames Collins} catch (Exception $e) { 107*12a6b3a2SJames Collins error_log("[css.php] Exception: " . $e->getMessage()); 108*12a6b3a2SJames Collins http_response_code(500); 1093cffc6f4SJames Collins header('Content-Type: text/css; charset=utf-8'); 110*12a6b3a2SJames Collins echo "/* An error occurred while processing the CSS. */"; 1112393fff5SJames Collins}