1<?php 2/** 3 * Utilities for collecting data from config files 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Harry Fuecks <hfuecks@gmail.com> 7 */ 8 9/* 10 * line prefix used to negate single value config items 11 * (scheme.conf & stopwords.conf), e.g. 12 * !gopher 13 */ 14const DOKU_CONF_NEGATION = '!'; 15 16/** 17 * Returns the (known) extension and mimetype of a given filename 18 * 19 * If $knownonly is true (the default), then only known extensions 20 * are returned. 21 * 22 * @author Andreas Gohr <andi@splitbrain.org> 23 * 24 * @param string $file file name 25 * @param bool $knownonly 26 * @return array with extension, mimetype and if it should be downloaded 27 */ 28function mimetype($file, $knownonly=true){ 29 $mtypes = getMimeTypes(); // known mimetypes 30 $ext = strrpos($file, '.'); 31 if ($ext === false) { 32 return array(false, false, false); 33 } 34 $ext = strtolower(substr($file, $ext + 1)); 35 if (!isset($mtypes[$ext])){ 36 if ($knownonly) { 37 return array(false, false, false); 38 } else { 39 return array($ext, 'application/octet-stream', true); 40 } 41 } 42 if($mtypes[$ext][0] == '!'){ 43 return array($ext, substr($mtypes[$ext],1), true); 44 }else{ 45 return array($ext, $mtypes[$ext], false); 46 } 47} 48 49/** 50 * returns a hash of mimetypes 51 * 52 * @author Andreas Gohr <andi@splitbrain.org> 53 */ 54function getMimeTypes() { 55 static $mime = null; 56 if ( !$mime ) { 57 $mime = retrieveConfig('mime','confToHash'); 58 $mime = array_filter($mime); 59 } 60 return $mime; 61} 62 63/** 64 * returns a hash of acronyms 65 * 66 * @author Harry Fuecks <hfuecks@gmail.com> 67 */ 68function getAcronyms() { 69 static $acronyms = null; 70 if ( !$acronyms ) { 71 $acronyms = retrieveConfig('acronyms','confToHash'); 72 $acronyms = array_filter($acronyms, 'strlen'); 73 } 74 return $acronyms; 75} 76 77/** 78 * returns a hash of smileys 79 * 80 * @author Harry Fuecks <hfuecks@gmail.com> 81 */ 82function getSmileys() { 83 static $smileys = null; 84 if ( !$smileys ) { 85 $smileys = retrieveConfig('smileys','confToHash'); 86 $smileys = array_filter($smileys, 'strlen'); 87 } 88 return $smileys; 89} 90 91/** 92 * returns a hash of entities 93 * 94 * @author Harry Fuecks <hfuecks@gmail.com> 95 */ 96function getEntities() { 97 static $entities = null; 98 if ( !$entities ) { 99 $entities = retrieveConfig('entities','confToHash'); 100 $entities = array_filter($entities, 'strlen'); 101 } 102 return $entities; 103} 104 105/** 106 * returns a hash of interwikilinks 107 * 108 * @author Harry Fuecks <hfuecks@gmail.com> 109 */ 110function getInterwiki() { 111 static $wikis = null; 112 if ( !$wikis ) { 113 $wikis = retrieveConfig('interwiki','confToHash',array(true)); 114 $wikis = array_filter($wikis, 'strlen'); 115 116 //add sepecial case 'this' 117 $wikis['this'] = DOKU_URL.'{NAME}'; 118 } 119 return $wikis; 120} 121 122/** 123 * Returns the jquery script URLs for the versions defined in lib/scripts/jquery/versions 124 * 125 * @trigger CONFUTIL_CDN_SELECT 126 * @return array 127 */ 128function getCdnUrls() { 129 global $conf; 130 131 // load version info 132 $versions = array(); 133 $lines = file(DOKU_INC . 'lib/scripts/jquery/versions'); 134 foreach($lines as $line) { 135 $line = trim(preg_replace('/#.*$/', '', $line)); 136 if($line === '') continue; 137 list($key, $val) = explode('=', $line, 2); 138 $key = trim($key); 139 $val = trim($val); 140 $versions[$key] = $val; 141 } 142 143 $src = array(); 144 $data = array( 145 'versions' => $versions, 146 'src' => &$src 147 ); 148 $event = new Doku_Event('CONFUTIL_CDN_SELECT', $data); 149 if($event->advise_before()) { 150 if(!$conf['jquerycdn']) { 151 $jqmod = md5(join('-', $versions)); 152 $src[] = DOKU_BASE . 'lib/exe/jquery.php' . '?tseed=' . $jqmod; 153 } elseif($conf['jquerycdn'] == 'jquery') { 154 $src[] = sprintf('https://code.jquery.com/jquery-%s.min.js', $versions['JQ_VERSION']); 155 $src[] = sprintf('https://code.jquery.com/jquery-migrate-%s.min.js', $versions['JQM_VERSION']); 156 $src[] = sprintf('https://code.jquery.com/ui/%s/jquery-ui.min.js', $versions['JQUI_VERSION']); 157 } elseif($conf['jquerycdn'] == 'cdnjs') { 158 $src[] = sprintf( 159 'https://cdnjs.cloudflare.com/ajax/libs/jquery/%s/jquery.min.js', 160 $versions['JQ_VERSION'] 161 ); 162 $src[] = sprintf( 163 'https://cdnjs.cloudflare.com/ajax/libs/jquery-migrate/%s/jquery-migrate.min.js', 164 $versions['JQM_VERSION'] 165 ); 166 $src[] = sprintf( 167 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/%s/jquery-ui.min.js', 168 $versions['JQUI_VERSION'] 169 ); 170 } 171 } 172 $event->advise_after(); 173 174 return $src; 175} 176 177/** 178 * returns array of wordblock patterns 179 * 180 */ 181function getWordblocks() { 182 static $wordblocks = null; 183 if ( !$wordblocks ) { 184 $wordblocks = retrieveConfig('wordblock','file',null,'array_merge_with_removal'); 185 } 186 return $wordblocks; 187} 188 189/** 190 * Gets the list of configured schemes 191 * 192 * @return array the schemes 193 */ 194function getSchemes() { 195 static $schemes = null; 196 if ( !$schemes ) { 197 $schemes = retrieveConfig('scheme','file',null,'array_merge_with_removal'); 198 $schemes = array_map('trim', $schemes); 199 $schemes = preg_replace('/^#.*/', '', $schemes); 200 $schemes = array_filter($schemes); 201 } 202 return $schemes; 203} 204 205/** 206 * Builds a hash from an array of lines 207 * 208 * If $lower is set to true all hash keys are converted to 209 * lower case. 210 * 211 * @author Harry Fuecks <hfuecks@gmail.com> 212 * @author Andreas Gohr <andi@splitbrain.org> 213 * @author Gina Haeussge <gina@foosel.net> 214 * 215 * @param array $lines 216 * @param bool $lower 217 * 218 * @return array 219 */ 220function linesToHash($lines, $lower = false) { 221 $conf = array(); 222 // remove BOM 223 if(isset($lines[0]) && substr($lines[0], 0, 3) == pack('CCC', 0xef, 0xbb, 0xbf)) 224 $lines[0] = substr($lines[0], 3); 225 foreach($lines as $line) { 226 //ignore comments (except escaped ones) 227 $line = preg_replace('/(?<![&\\\\])#.*$/', '', $line); 228 $line = str_replace('\\#', '#', $line); 229 $line = trim($line); 230 if($line === '') continue; 231 $line = preg_split('/\s+/', $line, 2); 232 $line = array_pad($line, 2, ''); 233 // Build the associative array 234 if($lower) { 235 $conf[strtolower($line[0])] = $line[1]; 236 } else { 237 $conf[$line[0]] = $line[1]; 238 } 239 } 240 241 return $conf; 242} 243 244/** 245 * Builds a hash from a configfile 246 * 247 * If $lower is set to true all hash keys are converted to 248 * lower case. 249 * 250 * @author Harry Fuecks <hfuecks@gmail.com> 251 * @author Andreas Gohr <andi@splitbrain.org> 252 * @author Gina Haeussge <gina@foosel.net> 253 * 254 * @param string $file 255 * @param bool $lower 256 * 257 * @return array 258 */ 259function confToHash($file,$lower=false) { 260 $conf = array(); 261 $lines = @file( $file ); 262 if ( !$lines ) return $conf; 263 264 return linesToHash($lines, $lower); 265} 266 267/** 268 * Read a json config file into an array 269 * 270 * @param string $file 271 * @return array 272 */ 273function jsonToArray($file) 274{ 275 $json = file_get_contents($file); 276 277 $conf = json_decode($json, true); 278 279 if ($conf === null) { 280 return []; 281 } 282 283 return $conf; 284} 285 286/** 287 * Retrieve the requested configuration information 288 * 289 * @author Chris Smith <chris@jalakai.co.uk> 290 * 291 * @param string $type the configuration settings to be read, must correspond to a key/array in $config_cascade 292 * @param callback $fn the function used to process the configuration file into an array 293 * @param array $params optional additional params to pass to the callback 294 * @param callback $combine the function used to combine arrays of values read from different configuration files; 295 * the function takes two parameters, 296 * $combined - the already read & merged configuration values 297 * $new - array of config values from the config cascade file being currently processed 298 * and returns an array of the merged configuration values. 299 * @return array configuration values 300 */ 301function retrieveConfig($type,$fn,$params=null,$combine='array_merge') { 302 global $config_cascade; 303 304 if(!is_array($params)) $params = array(); 305 306 $combined = array(); 307 if (!is_array($config_cascade[$type])) trigger_error('Missing config cascade for "'.$type.'"',E_USER_WARNING); 308 foreach (array('default','local','protected') as $config_group) { 309 if (empty($config_cascade[$type][$config_group])) continue; 310 foreach ($config_cascade[$type][$config_group] as $file) { 311 if (file_exists($file)) { 312 $config = call_user_func_array($fn,array_merge(array($file),$params)); 313 $combined = $combine($combined, $config); 314 } 315 } 316 } 317 318 return $combined; 319} 320 321/** 322 * Include the requested configuration information 323 * 324 * @author Chris Smith <chris@jalakai.co.uk> 325 * 326 * @param string $type the configuration settings to be read, must correspond to a key/array in $config_cascade 327 * @return array list of files, default before local before protected 328 */ 329function getConfigFiles($type) { 330 global $config_cascade; 331 $files = array(); 332 333 if (!is_array($config_cascade[$type])) trigger_error('Missing config cascade for "'.$type.'"',E_USER_WARNING); 334 foreach (array('default','local','protected') as $config_group) { 335 if (empty($config_cascade[$type][$config_group])) continue; 336 $files = array_merge($files, $config_cascade[$type][$config_group]); 337 } 338 339 return $files; 340} 341 342/** 343 * check if the given action was disabled in config 344 * 345 * @author Andreas Gohr <andi@splitbrain.org> 346 * @param string $action 347 * @returns boolean true if enabled, false if disabled 348 */ 349function actionOK($action){ 350 static $disabled = null; 351 if(is_null($disabled) || defined('SIMPLE_TEST')){ 352 global $conf; 353 /** @var DokuWiki_Auth_Plugin $auth */ 354 global $auth; 355 356 // prepare disabled actions array and handle legacy options 357 $disabled = explode(',',$conf['disableactions']); 358 $disabled = array_map('trim',$disabled); 359 if((isset($conf['openregister']) && !$conf['openregister']) || is_null($auth) || !$auth->canDo('addUser')) { 360 $disabled[] = 'register'; 361 } 362 if((isset($conf['resendpasswd']) && !$conf['resendpasswd']) || is_null($auth) || !$auth->canDo('modPass')) { 363 $disabled[] = 'resendpwd'; 364 } 365 if((isset($conf['subscribers']) && !$conf['subscribers']) || is_null($auth)) { 366 $disabled[] = 'subscribe'; 367 } 368 if (is_null($auth) || !$auth->canDo('Profile')) { 369 $disabled[] = 'profile'; 370 } 371 if (is_null($auth) || !$auth->canDo('delUser')) { 372 $disabled[] = 'profile_delete'; 373 } 374 if (is_null($auth)) { 375 $disabled[] = 'login'; 376 } 377 if (is_null($auth) || !$auth->canDo('logout')) { 378 $disabled[] = 'logout'; 379 } 380 $disabled = array_unique($disabled); 381 } 382 383 return !in_array($action,$disabled); 384} 385 386/** 387 * check if headings should be used as link text for the specified link type 388 * 389 * @author Chris Smith <chris@jalakai.co.uk> 390 * 391 * @param string $linktype 'content'|'navigation', content applies to links in wiki text 392 * navigation applies to all other links 393 * @return boolean true if headings should be used for $linktype, false otherwise 394 */ 395function useHeading($linktype) { 396 static $useHeading = null; 397 if(defined('DOKU_UNITTEST')) $useHeading = null; // don't cache during unit tests 398 399 if (is_null($useHeading)) { 400 global $conf; 401 402 if (!empty($conf['useheading'])) { 403 switch ($conf['useheading']) { 404 case 'content': 405 $useHeading['content'] = true; 406 break; 407 408 case 'navigation': 409 $useHeading['navigation'] = true; 410 break; 411 default: 412 $useHeading['content'] = true; 413 $useHeading['navigation'] = true; 414 } 415 } else { 416 $useHeading = array(); 417 } 418 } 419 420 return (!empty($useHeading[$linktype])); 421} 422 423/** 424 * obscure config data so information isn't plain text 425 * 426 * @param string $str data to be encoded 427 * @param string $code encoding method, values: plain, base64, uuencode. 428 * @return string the encoded value 429 */ 430function conf_encodeString($str,$code) { 431 switch ($code) { 432 case 'base64' : return '<b>'.base64_encode($str); 433 case 'uuencode' : return '<u>'.convert_uuencode($str); 434 case 'plain': 435 default: 436 return $str; 437 } 438} 439/** 440 * return obscured data as plain text 441 * 442 * @param string $str encoded data 443 * @return string plain text 444 */ 445function conf_decodeString($str) { 446 switch (substr($str,0,3)) { 447 case '<b>' : return base64_decode(substr($str,3)); 448 case '<u>' : return convert_uudecode(substr($str,3)); 449 default: // not encoded (or unknown) 450 return $str; 451 } 452} 453 454/** 455 * array combination function to remove negated values (prefixed by !) 456 * 457 * @param array $current 458 * @param array $new 459 * 460 * @return array the combined array, numeric keys reset 461 */ 462function array_merge_with_removal($current, $new) { 463 foreach ($new as $val) { 464 if (substr($val,0,1) == DOKU_CONF_NEGATION) { 465 $idx = array_search(trim(substr($val,1)),$current); 466 if ($idx !== false) { 467 unset($current[$idx]); 468 } 469 } else { 470 $current[] = trim($val); 471 } 472 } 473 474 return array_slice($current,0); 475} 476//Setup VIM: ex: et ts=4 : 477