gctools-outilsgc /
gcconnex
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | namespace Elgg; |
||
| 3 | |||
| 4 | /** |
||
| 5 | * Simplecache handler |
||
| 6 | * |
||
| 7 | * @access private |
||
| 8 | * |
||
| 9 | * @package Elgg.Core |
||
| 10 | */ |
||
| 11 | class CacheHandler { |
||
| 12 | |||
| 13 | protected $config; |
||
| 14 | |||
| 15 | /** |
||
| 16 | * Constructor |
||
| 17 | * |
||
| 18 | * @param \stdClass $config Elgg config object |
||
| 19 | */ |
||
| 20 | 6 | public function __construct($config) { |
|
| 21 | 6 | $this->config = $config; |
|
| 22 | 6 | } |
|
| 23 | |||
| 24 | /** |
||
| 25 | * Handle a request for a cached view |
||
| 26 | * |
||
| 27 | * @param array $get_vars $_GET variables |
||
| 28 | * @param array $server_vars $_SERVER variables |
||
| 29 | * @return void |
||
| 30 | */ |
||
| 31 | public function handleRequest($get_vars, $server_vars) { |
||
| 32 | if (empty($get_vars['request'])) { |
||
| 33 | $this->send403(); |
||
| 34 | } |
||
| 35 | $request = $this->parseRequestVar($get_vars['request']); |
||
| 36 | if (!$request) { |
||
|
0 ignored issues
–
show
|
|||
| 37 | $this->send403(); |
||
| 38 | } |
||
| 39 | $ts = $request['ts']; |
||
| 40 | $view = $request['view']; |
||
| 41 | $viewtype = $request['viewtype']; |
||
| 42 | |||
| 43 | $this->sendContentType($view); |
||
| 44 | |||
| 45 | // this may/may not have to connect to the DB |
||
| 46 | $this->setupSimplecache(); |
||
| 47 | |||
| 48 | if (!$this->config->simplecache_enabled) { |
||
| 49 | $this->loadEngine(); |
||
| 50 | if (!_elgg_is_view_cacheable($view)) { |
||
| 51 | $this->send403(); |
||
| 52 | } else { |
||
| 53 | echo $this->renderView($view, $viewtype); |
||
| 54 | } |
||
| 55 | exit; |
||
| 56 | } |
||
| 57 | |||
| 58 | $etag = "\"$ts\""; |
||
| 59 | // If is the same ETag, content didn't change. |
||
| 60 | View Code Duplication | if (isset($server_vars['HTTP_IF_NONE_MATCH'])) { |
|
| 61 | // strip -gzip for #9427 |
||
| 62 | $if_none_match = str_replace('-gzip', '', trim($server_vars['HTTP_IF_NONE_MATCH'])); |
||
| 63 | if ($if_none_match === $etag) { |
||
| 64 | header("HTTP/1.1 304 Not Modified"); |
||
| 65 | header("ETag: $etag"); |
||
| 66 | exit; |
||
| 67 | } |
||
| 68 | } |
||
| 69 | |||
| 70 | $filename = $this->config->dataroot . 'views_simplecache/' . md5("$viewtype|$view"); |
||
| 71 | if (file_exists($filename)) { |
||
| 72 | $this->sendCacheHeaders($etag); |
||
| 73 | readfile($filename); |
||
| 74 | exit; |
||
| 75 | } |
||
| 76 | |||
| 77 | $this->loadEngine(); |
||
| 78 | |||
| 79 | elgg_set_viewtype($viewtype); |
||
| 80 | if (!_elgg_is_view_cacheable($view)) { |
||
| 81 | $this->send403(); |
||
| 82 | } |
||
| 83 | |||
| 84 | $cache_timestamp = (int)_elgg_services()->config->get('lastcache'); |
||
| 85 | |||
| 86 | if ($cache_timestamp == $ts) { |
||
| 87 | $this->sendCacheHeaders($etag); |
||
| 88 | |||
| 89 | $content = $this->getProcessedView($view, $viewtype); |
||
| 90 | |||
| 91 | $dir_name = $this->config->dataroot . 'views_simplecache/'; |
||
| 92 | if (!is_dir($dir_name)) { |
||
| 93 | mkdir($dir_name, 0700); |
||
| 94 | } |
||
| 95 | |||
| 96 | file_put_contents($filename, $content); |
||
| 97 | } else { |
||
| 98 | // if wrong timestamp, don't send HTTP cache |
||
| 99 | $content = $this->renderView($view, $viewtype); |
||
| 100 | } |
||
| 101 | |||
| 102 | echo $content; |
||
| 103 | exit; |
||
| 104 | } |
||
| 105 | |||
| 106 | /** |
||
| 107 | * Parse a request |
||
| 108 | * |
||
| 109 | * @param string $request_var Request URL |
||
| 110 | * @return array Cache parameters (empty array if failure) |
||
| 111 | */ |
||
| 112 | 6 | public function parseRequestVar($request_var) { |
|
| 113 | // no '..' |
||
| 114 | 6 | if (false !== strpos($request_var, '..')) { |
|
| 115 | 1 | return array(); |
|
| 116 | } |
||
| 117 | // only alphanumeric characters plus /, ., -, and _ |
||
| 118 | 5 | if (preg_match('#[^a-zA-Z0-9/\.\-_]#', $request_var)) { |
|
| 119 | 1 | return array(); |
|
| 120 | } |
||
| 121 | |||
| 122 | // testing showed regex to be marginally faster than array / string functions over 100000 reps |
||
| 123 | // it won't make a difference in real life and regex is easier to read. |
||
| 124 | // <ts>/<viewtype>/<name/of/view.and.dots>.<type> |
||
| 125 | 4 | if (!preg_match('#^/?([0-9]+)/([^/]+)/(.+)$#', $request_var, $matches)) { |
|
| 126 | 2 | return array(); |
|
| 127 | } |
||
| 128 | |||
| 129 | return array( |
||
| 130 | 2 | 'ts' => $matches[1], |
|
| 131 | 2 | 'viewtype' => $matches[2], |
|
| 132 | 2 | 'view' => $matches[3], |
|
| 133 | 2 | ); |
|
| 134 | } |
||
| 135 | |||
| 136 | /** |
||
| 137 | * Do a minimal engine load |
||
| 138 | * |
||
| 139 | * @return void |
||
| 140 | */ |
||
| 141 | protected function setupSimplecache() { |
||
| 142 | if (!empty($this->config->dataroot) && isset($this->config->simplecache_enabled)) { |
||
| 143 | return; |
||
| 144 | } |
||
| 145 | |||
| 146 | $db_config = new Database\Config($this->config); |
||
| 147 | $db = new Database($db_config, new Logger(new PluginHooksService())); |
||
| 148 | |||
| 149 | try { |
||
| 150 | $rows = $db->getData(" |
||
| 151 | SELECT `name`, `value` |
||
| 152 | FROM {$db->getTablePrefix()}datalists |
||
| 153 | WHERE `name` IN ('dataroot', 'simplecache_enabled') |
||
| 154 | "); |
||
| 155 | if (!$rows) { |
||
|
0 ignored issues
–
show
The expression
$rows of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 156 | $this->send403('Cache error: unable to get the data root'); |
||
| 157 | } |
||
| 158 | } catch (\DatabaseException $e) { |
||
| 159 | if (0 === strpos($e->getMessage(), "Elgg couldn't connect")) { |
||
| 160 | $this->send403('Cache error: unable to connect to database server'); |
||
| 161 | } else { |
||
| 162 | $this->send403('Cache error: unable to connect to Elgg database'); |
||
| 163 | } |
||
| 164 | exit; // unnecessary, but helps PhpStorm understand |
||
| 165 | } |
||
| 166 | |||
| 167 | foreach ($rows as $row) { |
||
| 168 | $this->config->{$row->name} = $row->value; |
||
| 169 | } |
||
| 170 | |||
| 171 | if (empty($this->config->dataroot)) { |
||
| 172 | $this->send403('Cache error: unable to get the data root'); |
||
| 173 | } |
||
| 174 | } |
||
| 175 | |||
| 176 | /** |
||
| 177 | * Send cache headers |
||
| 178 | * |
||
| 179 | * @param string $etag ETag value |
||
| 180 | * @return void |
||
| 181 | */ |
||
| 182 | protected function sendCacheHeaders($etag) { |
||
| 183 | header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+6 months")), true); |
||
| 184 | header("Pragma: public", true); |
||
| 185 | header("Cache-Control: public", true); |
||
| 186 | header("ETag: $etag"); |
||
| 187 | } |
||
| 188 | |||
| 189 | /** |
||
| 190 | * Send content type |
||
| 191 | * |
||
| 192 | * @param string $view The view name |
||
| 193 | * @return void |
||
| 194 | */ |
||
| 195 | protected function sendContentType($view) { |
||
| 196 | $segments = explode('/', $view, 2); |
||
| 197 | View Code Duplication | switch ($segments[0]) { |
|
| 198 | case 'css': |
||
| 199 | header("Content-Type: text/css;charset=utf-8"); |
||
| 200 | break; |
||
| 201 | case 'js': |
||
| 202 | header('Content-Type: text/javascript;charset=utf-8'); |
||
| 203 | break; |
||
| 204 | default: |
||
| 205 | header('Content-Type: text/html;charset=utf-8'); |
||
| 206 | } |
||
| 207 | } |
||
| 208 | |||
| 209 | /** |
||
| 210 | * Get the contents of a view for caching |
||
| 211 | * |
||
| 212 | * @param string $view The view name |
||
| 213 | * @param string $viewtype The viewtype |
||
| 214 | * @return string |
||
| 215 | * @see CacheHandler::renderView() |
||
| 216 | */ |
||
| 217 | protected function getProcessedView($view, $viewtype) { |
||
| 218 | $content = $this->renderView($view, $viewtype); |
||
| 219 | |||
| 220 | $hook_type = _elgg_get_view_filetype($view); |
||
| 221 | $hook_params = array( |
||
| 222 | 'view' => $view, |
||
| 223 | 'viewtype' => $viewtype, |
||
| 224 | 'view_content' => $content, |
||
| 225 | ); |
||
| 226 | return _elgg_services()->hooks->trigger('simplecache:generate', $hook_type, $hook_params, $content); |
||
| 227 | } |
||
| 228 | |||
| 229 | /** |
||
| 230 | * Render a view for caching |
||
| 231 | * |
||
| 232 | * @param string $view The view name |
||
| 233 | * @param string $viewtype The viewtype |
||
| 234 | * @return string |
||
| 235 | */ |
||
| 236 | protected function renderView($view, $viewtype) { |
||
| 237 | elgg_set_viewtype($viewtype); |
||
| 238 | |||
| 239 | if (!elgg_view_exists($view)) { |
||
| 240 | $this->send403(); |
||
| 241 | } |
||
| 242 | |||
| 243 | // disable error reporting so we don't cache problems |
||
| 244 | _elgg_services()->config->set('debug', null); |
||
| 245 | |||
| 246 | // @todo elgg_view() checks if the page set is done (isset($CONFIG->pagesetupdone)) and |
||
| 247 | // triggers an event if it's not. Calling elgg_view() here breaks submenus |
||
| 248 | // (at least) because the page setup hook is called before any |
||
| 249 | // contexts can be correctly set (since this is called before page_handler()). |
||
| 250 | // To avoid this, lie about $CONFIG->pagehandlerdone to force |
||
| 251 | // the trigger correctly when the first view is actually being output. |
||
| 252 | _elgg_services()->config->set('pagesetupdone', true); |
||
| 253 | |||
| 254 | return elgg_view($view); |
||
| 255 | } |
||
| 256 | |||
| 257 | /** |
||
| 258 | * Load the complete Elgg engine |
||
| 259 | * |
||
| 260 | * @return void |
||
| 261 | */ |
||
| 262 | protected function loadEngine() { |
||
| 263 | require_once dirname(dirname(dirname(__FILE__))) . "/start.php"; |
||
| 264 | } |
||
| 265 | |||
| 266 | /** |
||
| 267 | * Send an error message to requestor |
||
| 268 | * |
||
| 269 | * @param string $msg Optional message text |
||
| 270 | * @return void |
||
| 271 | */ |
||
| 272 | protected function send403($msg = 'Cache error: bad request') { |
||
| 273 | header('HTTP/1.1 403 Forbidden'); |
||
| 274 | echo $msg; |
||
| 275 | exit; |
||
| 276 | } |
||
| 277 | } |
||
| 278 | |||
| 279 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)or! empty(...)instead.