1<?php 2 3if (!defined('DOKU_PLUGIN')) die('meh'); 4require_once(DOKU_PLUGIN . 'siteexport/inc/settings.php'); 5require_once(DOKU_PLUGIN . 'siteexport/inc/debug.php'); 6 7class siteexport_functions extends DokuWiki_Plugin 8{ 9 public $debug = null; 10 public $settings = null; 11 12 public function __construct($init = true, $isAJAX = false) 13 { 14 if ($init) 15 { 16 $this->debug = new siteexport_debug(); 17 $this->debug->isAJAX = $isAJAX; 18 19 $this->settings = new settings_plugin_siteexport_settings($this); 20 $this->debug->message("Settings completed: zipFile", $this->settings->zipFile, 1); 21 } 22 } 23 24 public function getPluginName() 25 { 26 return 'siteexport'; 27 } 28 29 public function downloadURL() 30 { 31 $params = array('cache' => 'nocache', 'siteexport' => $this->settings->pattern); 32 33 if ($this->debug->debugLevel() < 5) { 34 // If debug, then debug! 35 $params['debug'] = $this->debug->debugLevel(); 36 } 37 38 return ml($this->settings->origZipFile, $params, true, '&'); 39 } 40 41 public function checkIfCacheFileExistsForFileWithPattern($file, $pattern) 42 { 43 if (!@file_exists($file)) 44 { 45 // If the cache File does not exist, move the newly created one over ... 46 $this->debug->message("'{$file}' does not exist. Checking original ZipFile", null, 3); 47 $newCacheFile = mediaFN($this->getSpecialExportFileName($this->settings->origZipFile, $pattern)); 48 49 if (!@file_exists($newCacheFile)) 50 { 51 $this->debug->message("The export must have gone wrong. The cached file does not exist.", array("pattern" => $pattern, "original File" => $this->settings->origZipFile, "expected cached file" => $newCacheFile), 3); 52 } 53 54 $status = io_rename($newCacheFile, $file); 55 $this->debug->message("had to move another original file over. Did it work? " . ($status ? 'Yes, it did.' : 'No, it did not.'), null, 2); 56 } else { 57 $this->debug->message("The file does exist!", $file, 2); 58 } 59 } 60 61 62 /** 63 * Returns an utf8 encoded Namespace for a Page and input Namespace 64 * @param $NS 65 * @param $PAGE 66 */ 67 function getNamespaceFromID($NS, &$PAGE) { 68 global $conf; 69 // Check current page - if its an NS add the startpage 70 $clean = true; 71 resolve_pageid(getNS($NS), $NS, $clean); 72 $NSa = explode(':', $NS); 73 if (!page_exists($NS) && array_pop($NSa) != strtolower($conf['start'])) { // Compare to lowercase since clean lowers it. 74 $NS .= ':' . $conf['start']; 75 resolve_pageid(getNS($NS), $NS, $clean); 76 } 77 78 $PAGE = noNS($NS); 79 $NS = getNS($NS); 80 81 return utf8_encodeFN(str_replace(':', '/', $NS)); 82 } 83 84 /** 85 * create a file name for the page 86 **/ 87 public function getSiteName($ID, $overrideRewrite = false) { 88 global $conf; 89 90 if (empty($ID)) return false; 91 92 // Remove extensions 93 if ($overrideRewrite) { 94 $ID = preg_replace("#\.(php|html)$#", '', $ID); 95 } 96 97 $url = $this->wl($this->cleanID($ID), null, true, null, null, $overrideRewrite); // this must be done with rewriting set to override 98 //$url = $this->wl($this->cleanID($ID), null, true); // this must be done with rewriting set to override 99 $uri = @parse_url($url); 100 if ($uri['path'][0] == '/') { 101 $uri['path'] = substr($uri['path'], 1); 102 } 103 104 return $this->shortenName($uri['path'] . '.' . $this->settings->fileType); 105 } 106 107 /** 108 * get the Title for the page 109 **/ 110 public function getSiteTitle($ID) { 111 if (useHeading('content') && $ID) { 112 $heading = p_get_first_heading($ID, true); 113 if ($heading) { 114 return $this->xmlEntities($heading); 115 } 116 } 117 $elements = explode(':', $ID); 118 return ucwords($this->xmlEntities(array_pop($elements))); 119 } 120 121 /** 122 * Encoding ()taken from DW - but without needing the renderer 123 **/ 124 public function xmlEntities($string) { 125 return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 126 } 127 128 /** 129 * Create name for the file inside the zip and the replacements 130 **/ 131 function shortenName($NAME) 132 { 133 $NS = $this->settings->exportNamespace; 134 $NAME = preg_replace("%^" . preg_quote(DOKU_BASE, '%') . "%", "", $NAME); 135 $NAME = preg_replace("%^((_media|_detail)/)?(" . preg_quote($NS, '%') . "/)?%", "", $NAME); 136 137 if (strstr($NAME, '%')) { $NAME = rawurldecode($NAME); } 138 139 $this->debug->message("Shortening file to '$NAME'", null, 1); 140 return $NAME; 141 } 142 143 /** 144 * Remove unwanted chars from ID 145 * 146 * Cleans a given ID to only use allowed characters. Accented characters are 147 * converted to unaccented ones 148 * 149 * @author Andreas Gohr <andi@splitbrain.org> 150 * @param string $raw_id The pageid to clean 151 * @param boolean $ascii Force ASCII 152 * @param boolean $media Allow leading or trailing _ for media files 153 */ 154 function cleanID($raw_id, $ascii = false, $media = false) { 155 global $conf; 156 global $lang; 157 static $sepcharpat = null; 158 159 global $cache_cleanid; 160 $cache = & $cache_cleanid; 161 162 // check if it's already in the memory cache 163 if (isset($cache[(string) $raw_id])) { 164 return $cache[(string) $raw_id]; 165 } 166 167 $sepchar = $conf['sepchar']; 168 if ($sepcharpat == null) // build string only once to save clock cycles 169 $sepcharpat = '#\\' . $sepchar . '+#'; 170 171 $id = trim((string) $raw_id); 172 // $id = utf8_strtolower($id); // NO LowerCase for us! 173 174 //alternative namespace seperator 175 $id = strtr($id, ';', ':'); 176 if ($conf['useslash']) { 177 $id = strtr($id, '/', ':'); 178 } else { 179 $id = strtr($id, '/', $sepchar); 180 } 181 182 if ($conf['deaccent'] == 2 || $ascii) $id = utf8_romanize($id); 183 if ($conf['deaccent'] || $ascii) $id = utf8_deaccent($id, -1); 184 185 // We want spaces to be preserved when they are in the link. 186 global $UTF8_SPECIAL_CHARS2; 187 $UTF8_SPECIAL_CHARS2_SAVE = (string) $UTF8_SPECIAL_CHARS2; 188 $UTF8_SPECIAL_CHARS2 = str_replace(' ', '', $UTF8_SPECIAL_CHARS2); 189 190 //remove specials 191 $id = utf8_stripspecials($id, $sepchar, '\*'); 192 $UTF8_SPECIAL_CHARS2 = $UTF8_SPECIAL_CHARS2_SAVE; 193 194 if ($ascii) $id = utf8_strip($id); 195 196 //clean up 197 $id = preg_replace($sepcharpat, $sepchar, $id); 198 $id = preg_replace('#:+#', ':', $id); 199 $id = ($media ? trim($id, ':.-') : trim($id, ':._-')); 200 $id = preg_replace('#:[:\._\-]+#', ':', $id); 201 202 $cache[(string) $raw_id] = $id; 203 return($id); 204 } 205 206 207 /** 208 * This builds a link to a wikipage - changed for internal use here 209 * 210 * It handles URL rewriting and adds additional parameter if 211 * given in $more 212 * 213 * @author Andreas Gohr <andi@splitbrain.org> 214 */ 215 216 function wl($id='',$more='',$abs=false,$sep='&', $IDexists=true, $overrideRewrite=false, $hadBase=false){ 217 global $conf; 218 219 $this->debug->message("Starting to build WL-URL for '$id'", $more, 1); 220 221 if(is_array($more)){ 222 223 $intermediateMore = ''; 224 foreach( $more as $key => $value) { 225 226 if ( strlen($intermediateMore) > 0 ) { 227 $intermediateMore .= $sep; 228 } 229 230 if ( !is_array($value) ) { 231 $intermediateMore .= rawurlencode($key) . '='; 232 $intermediateMore .= rawurlencode($value); 233 continue; 234 } 235 236 foreach( $value as $val ) { 237 if ( strlen($intermediateMore) > 0 ) { 238 $intermediateMore .= $sep; 239 } 240 241 $intermediateMore .= rawurlencode($key) . '[]='; 242 $intermediateMore .= rawurlencode($val); 243 } 244 } 245 246 $more = $intermediateMore; 247 } else { 248 $more = str_replace(',', $sep, $more); 249 } 250 251 $id = idfilter($id); 252 253 if ($abs) { 254 $xlink = DOKU_URL; 255 if (!$IDexists && !$hadBase) { // If the file does not exist, we have to remove the base. This link my be one to an parallel BASE. 256 $xlink = preg_replace('#' . DOKU_BASE . '$#', '', $xlink); 257 } 258 } else if ($IDexists || $hadBase) { // if the ID does exist, we may add the base. 259 $xlink = DOKU_BASE; 260 } else { 261 $xlink = ""; 262 } 263 264 // $this->debug->message("internal WL function Before Replacing: '$xlink'", array(DOKU_REL, DOKU_URL, DOKU_BASE, $xlink), 2); 265 $xlink = preg_replace('#(?<!http:|https:)//+#', '/', ($abs ? '' : '/') . "$xlink/"); // ensure slashes at beginning and ending, but strip doubles 266 $this->debug->message("'$xlink'", array(DOKU_REL, DOKU_URL, DOKU_BASE, $xlink), 2); 267 268 if ($overrideRewrite) { 269 $this->debug->message("Override enabled.", null, 1); 270 $id = strtr($id, ':', '/'); 271 272 $xlink .= $id; 273 if ($more) $xlink .= '?' . $more; 274 } else { 275 if ($conf['userewrite'] == 2) { 276 $xlink .= DOKU_SCRIPT . '/' . $id; 277 if ($more) $xlink .= '?' . $more; 278 }elseif ($conf['userewrite']) { 279 $xlink .= $id; 280 if ($more) $xlink .= '?' . $more; 281 }elseif ($id) { 282 $xlink .= DOKU_SCRIPT . '?id=' . $id; 283 if ($more) $xlink .= $sep . $more; 284 } else { 285 $xlink .= DOKU_SCRIPT; 286 if ($more) $xlink .= '?' . $more; 287 } 288 } 289 290 $this->debug->message("internal WL function result: '$xlink'", null, 2); 291 292 return $xlink; 293 } 294 295 /** 296 * Create the export file name - this is the file where everything is being stored 297 * @param $FILE 298 * @param $PATTERN - additional pattern for re-using old files 299 */ 300 public function getSpecialExportFileName($FILE, $PATTERN = null) { 301 302 if (empty($FILE)) 303 { 304 $FILE = $this->settings->origZipFile; 305 } 306 307 if (empty($PATTERN) && empty($this->settings->pattern)) { 308 $this->debug->message("Generating an internal md5 pattern. This will go wrong - and won't cache properly.", null, 3); 309 $PATTERN = md5(microtime(false)); 310 } 311 312 // Set Pattern Global for other stuff 313 if (empty($this->settings->pattern)) { 314 $this->settings['pattern'] = $PATTERN; 315 } else { 316 $PATTERN = $this->settings->pattern; 317 } 318 319 $FA = explode('.', $FILE); 320 $EXT = array_pop($FA); 321 array_push($FA, 'auto'); 322 array_push($FA, $PATTERN); 323 array_push($FA, $EXT); 324 325 $fileName = implode('.', $FA); 326 $this->debug->message("Export Filename for '$FILE' will be: '$fileName'", null, 2); 327 return $fileName; 328 } 329 330 public function getCacheFileNameForPattern($PATTERN = null) 331 { 332 if ($PATTERN == null) { 333 $PATTERN = $this->settings->pattern; 334 } 335 336 return getCacheName($this->getSpecialExportFileName($this->settings->origZipFile, $PATTERN), '.' . basename(mediaFN($this->settings->origZipFile))); 337 } 338 339 /** 340 * @param integer $counter 341 */ 342 function startRedirctProcess($counter) { 343 global $ID; 344 345 $URL = wl($ID); 346 347 $additionalParameters = $_REQUEST; 348 $additionalParameters['startcounter'] = $counter; 349 $additionalParameters['pattern'] = $this->settings->pattern; 350 351 unset($additionalParameters['id']); 352 unset($additionalParameters['u']); 353 unset($additionalParameters['p']); 354 unset($additionalParameters['r']); 355 unset($additionalParameters['http_credentials']); 356 357 $this->addAdditionalParametersToURL($URL, $additionalParameters); 358 $this->debug->message("Redirecting to '$URL'", null, 2); 359 360 send_redirect($URL); 361 exit(0); // Should not be reached, but anyways 362 } 363 364 /** 365 * Builds additional Parameters into the URL given 366 * @param $URL 367 * @param $newAdditionalParameters 368 */ 369 function addAdditionalParametersToURL(&$URL, $newAdditionalParameters) { 370 371 // Add additionalParameters 372 if (!empty($newAdditionalParameters)) { 373 foreach ($newAdditionalParameters as $key => $value) { 374 if (empty($key) || empty($value)) { continue; } 375 376 $append = ''; 377 378 if (is_array($value)) { 379 foreach (array_values($value) as $aValue) { // Array Handling 380 $URL .= (strstr($URL, '?') ? '&' : '?') . $key . "[]=$aValue"; 381 } 382 } else { 383 $append = "$key=$value"; 384 $URL .= empty($append) || strstr($URL, $append) ? '' : (strstr($URL, '?') ? '&' : '?') . $append; 385 } 386 387 } 388 } 389 } 390 391 /** 392 * Cleans the wiki variables and returns a rebuild URL that has the new variables at hand 393 * @param $data 394 */ 395 function prepare_POSTData($data) 396 { 397 $NS = !empty($data['ns']) ? $data['ns'] : $data['id']; 398 399 $this->removeWikiVariables($data); 400 $data['do'] = 'siteexport'; 401 $additionalKeys = ''; 402 403 ksort($data); 404 405 $this->debug->message("Prepared POST data:", $data, 1); 406 407 foreach ($data as $key => $value) { 408 409 if (!is_array($value)) { continue; } 410 $this->debug->message("Found inner Array:", $value, 1); 411 412 asort($value); 413 foreach ($value as $innerKey => $aValue) 414 { 415 if (is_numeric($innerKey)) 416 { 417 $innerKey = ''; 418 } 419 420 $additionalKeys .= "&$key" . "[$innerKey]=$aValue"; 421 } 422 423 unset($data[$key]); 424 } 425 426 return wl($NS, $data, true, '&') . $additionalKeys; 427 } 428 429 /** 430 * Parses a String into a $_REQUEST Like variant. You have to tell if a decode of the values is needed 431 * @param $inputArray 432 * @param $decode 433 */ 434 public function parseStringToRequestArray($inputArray, $decode=false) 435 { 436 global $plugin_controller; 437 438 $outputArray = $inputArray; 439 if ( !is_array($inputArray) ) 440 { 441 $intermediate = str_replace("&", "&", $inputArray); 442 443 $outputArray = array(); 444 foreach( explode("&", $intermediate) as $param ) { 445 list($key, $value) = explode("=", $param, 2); 446 447 // This is needed if we do want to calculate $_REQUEST for a non HTTP-Request 448 if ( $decode) 449 { 450 $value = urldecode($value); 451 } 452 453 if ( empty($key) ) { continue; } // Don't check on Value, because there may be only the key that should be preserved 454 455 if ( substr($key, -2) == '[]' ) { 456 $key = substr($key, 0, -2); 457 if ( !is_array($outputArray[$key]) ) { 458 $outputArray[$key] = array(); 459 } 460 461 array_push($outputArray[$key], $value); // Array Handling 462 } else { 463 $outputArray[$key] = $value; 464 } 465 } 466 } 467 468 if (!empty($outputArray['diPlu'])) { 469 470 $allPlugins = array(); 471 foreach ($plugin_controller->getList(null, true) as $plugin) { 472 // check for CSS or JS 473 if (!file_exists(DOKU_PLUGIN . "$plugin/script.js") && !file_exists(DOKU_PLUGIN . "$p/style.css")) { continue; } 474 $allPlugins[] = $plugin; 475 } 476 477 if (count($outputArray['diPlu']) > (count($allPlugins)/2)) { 478 $outputArray['diInv'] = 1; 479 $outputArray['diPlu'] = array_diff($allPlugins, $outputArray['diPlu']); 480 } 481 } 482 483 return $outputArray; 484 } 485 486 /** 487 * Remove certain fields from the list. 488 * @param $removeArray 489 * @param $advanced 490 * @param $isString 491 */ 492 function removeWikiVariables(&$removeArray, $advanced = false, $isString = false) { 493 494 $removeArray = $this->parseStringToRequestArray($removeArray); 495 496 // 2010-08-23 - If there is still the media set, retain the id for e.g. detail.php 497 if (!isset($removeArray['media'])) { 498 unset($removeArray['id']); 499 } 500 501 unset($removeArray['do']); 502 unset($removeArray['ns']); 503 unset($removeArray['call']); 504 unset($removeArray['sectok']); 505 unset($removeArray['rndval']); 506 unset($removeArray['tseed']); 507 unset($removeArray['http_credentials']); 508 unset($removeArray['u']); 509 unset($removeArray['p']); 510 unset($removeArray['r']); 511 unset($removeArray['base']); 512 unset($removeArray['siteexport']); 513 unset($removeArray['DokuWiki']); 514 515 if ($removeArray['renderer'] == 'xhtml') { 516 $removeArray['do'] = 'export_' . $removeArray['renderer']; 517 unset($removeArray['renderer']); 518 } 519 520 // Keep custom options 521 if (is_array($removeArray['customoptionname']) && is_array($removeArray['customoptionvalue']) && count($removeArray['customoptionname']) == count($removeArray['customoptionvalue'])) 522 { 523 for ($index = 0; $index < count($removeArray['customoptionname']); $index++) 524 { 525 $removeArray[$removeArray['customoptionname'][$index]] = $removeArray['customoptionvalue'][$index]; 526 } 527 unset($removeArray['customoptionname']); 528 unset($removeArray['customoptionvalue']); 529 } 530 531 if ($advanced) { 532 if ($removeArray['renderer'] != 'xhtml' && !empty($removeArray['renderer'])) { 533 $removeArray['do'] = 'export_' . $removeArray['renderer']; 534 } 535 536 // 2010-08-25 - Need fakeMedia for some _detail cases with rewrite = 2 537 if (isset($removeArray['fakeMedia'])) { 538 unset($removeArray['media']); 539 unset($removeArray['fakeMedia']); 540 } 541 542 /* remove internal params */ 543 unset($removeArray['ens']); 544 unset($removeArray['renderer']); 545 unset($removeArray['site']); 546 unset($removeArray['namespace']); 547 unset($removeArray['exportbody']); 548 unset($removeArray['addParams']); 549 unset($removeArray['template']); 550 unset($removeArray['eclipseDocZip']); 551 unset($removeArray['useTocFile']); 552 unset($removeArray['JavaHelpDocZip']); 553 unset($removeArray['depth']); 554 unset($removeArray['depthType']); 555 unset($removeArray['startcounter']); 556 unset($removeArray['pattern']); 557 unset($removeArray['TOCMapWithoutTranslation']); 558 // unset($removeArray['disableCache']); 559 unset($removeArray['debug']); 560 } 561 562 if ($isString && is_array($removeArray)) { 563 $intermediate = $removeArray; 564 $removeArray = array(); 565 566 foreach ($intermediate as $key => $value) { 567 if (is_array($value)) { 568 foreach (array_values($value) as $aValue) { // Array Handling 569 $removeArray[] = $key . "[]=$aValue"; 570 } 571 } else { 572 $value = trim($value); 573 574 $removeArray[] = "$key" . (((empty($value) && intval($value) !== 0)) || $value == '' ? '' : "=$value"); // If the Value is empty, the Key must be preserved 575 } 576 } 577 578 //$removeArray = implode( ($this->settings->fileType == 'pdf' ? "&" : "&"), $removeArray); 579 $removeArray = implode("&", $removeArray); // The & made problems with the HTTPClient / Apache. It should not be a problem to have & 580 } 581 } 582 583 /** 584 * returns a hashed name for the parameters 585 * @param $parameters 586 */ 587 public function hashNameForParameters($parameters) 588 { 589 return md5($parameters); 590 } 591 592 /** 593 * Takes an URL and transforms it into the path+query part 594 * Used several times, e.g. for genering the hash for the cache file 595 * @param string $url 596 */ 597 public function urlToPathAndParams($url) 598 { 599 $query = parse_url($url, PHP_URL_QUERY); 600 $path = preg_replace(":^" . DOKU_REL . ":", "", parse_url($url, PHP_URL_PATH)); 601 return "{$path}?{$query}"; 602 } 603 604 /** 605 * Transforms an $_REQUEST into a Hash that can be used for cron and cache file 606 * @param $request 607 */ 608 public function requestParametersToCacheHash($request) 609 { 610 $params = $this->urlToPathAndParams($this->prepare_POSTData($request)); 611 $this->debug->message("Calculated the following Cache Hash URL: ", $params, 2); 612 return $this->hashNameForParameters($params); 613 } 614 615 /** 616 * Check a replaceURL against a baseURL - and make the replaceURL relative against it 617 * @param replaceURL - URL which will be made relative if needed 618 * @param baseURL - URL which is the reference to be made relative against 619 * @param string $replaceURL 620 */ 621 public function getRelativeURL($replaceURL, $baseURL, $existingPageID = null) 622 { 623 // Base is always absolute without anything at the beginning 624 if (preg_match("#^(\.\./)+#", $baseURL)) { 625 $this->debug->message("The baseURL was not absolute.", $baseURL, 1); 626 return $replaceURL; 627 } 628 629 $origReplaceURL = $replaceURL; 630 $replaceURL = preg_replace("#^(\.\./)+#", '', $replaceURL); 631 632 // Remove ../ at beginning to get the absolute path 633 if ($replaceURL == $origReplaceURL) { 634 $this->debug->message("The replaceURL was already absolute.", $replaceURL, 1); 635 return $replaceURL; 636 } 637 638 $replaceParts = explode('/', $replaceURL); 639 $fileName = array_pop($replaceParts); // Get file 640 641 $baseParts = explode('/', $baseURL); 642 array_pop($baseParts); // Remove file. We only need the path to this location. 643 644 $this->debug->message("State before kicking.", array($replaceParts, $baseParts), 1); 645 646 // Kick all ../ 647 $originalBasePartsCount = count($baseParts); 648 while (count($replaceParts) > 0 && count($baseParts) > 0) { 649 650 if ($baseParts[0] == $replaceParts[0]) { 651 // Beginning is OK, so remove it. 652 array_shift($replaceParts); 653 array_shift($baseParts); 654 } else { 655 break; 656 } 657 658 } 659 660 $this->debug->message("Found URL '{$replaceURL}' that is relative to current page '{$baseURL}'.", array($replaceParts, $baseParts), 1); 661 662 // Remove everything that is identical 663 $replaceParts[] = $fileName; 664 665 // do the final link calculation 666 $finalLink = str_repeat('../', count($baseParts)) . implode('/', $replaceParts); 667 668 // find out if this is outside of our own export context, becond the baseURL 669 $offsiteTemplate = $this->getConf("offSiteLinkTemplate"); 670 $this->debug->message("Checking for offsite links", array( 671 "baseParts" => count($baseParts), 672 "originalBaseParts" => $originalBasePartsCount, 673 "ExistingPageID" => $existingPageID, 674 "offsiteTemplate" => $offsiteTemplate 675 ), 1); 676 if (count($baseParts) == $originalBasePartsCount && $existingPageID != null && !empty($offsiteTemplate)) { 677 678 $offsiteTemplate = str_replace('RAWID', $existingPageID, $offsiteTemplate); 679 680 $check = null; 681 $offsiteTemplate = str_replace('CONTEXTID', array_pop($this->getMapID($existingPageID, null, $check)), $offsiteTemplate); 682 $offsiteTemplate = str_replace('LINK', $finalLink, $offsiteTemplate); 683 684 $this->debug->message("Replacing finalLink '${finalLink}' with offsiteLink '${offsiteTemplate}'", null, 1); 685 $finalLink = $offsiteTemplate; 686 } 687 688 return $finalLink; 689 } 690 691 function mapIDWithAnchor(&$n, $key, $postfix) 692 { 693 if (empty($postfix)) return; 694 $n .= '-' . $postfix; 695 } 696 697 function getMapID($elemID, $postfix, &$check) 698 { 699 $meta = p_get_metadata($elemID, 'context', true); 700 701 if (empty($meta['id'])) { 702 $title = empty($meta['title']) ? $this->getSiteTitle($elemID) : $meta['title']; 703 $meta['id'] = sectionID($this->cleanId(strtolower($title)), $check); 704 } 705 706 $mapID = explode('|', $meta['id']); 707 array_walk($mapID, array($this, 'mapIDWithAnchor'), $postfix); 708 709 return $mapID; 710 } 711 712 public function hasAuthentication() { 713 $user = $this->getConf('defaultAuthenticationUser'); 714 $password = $this->getConf('defaultAuthenticationPassword'); 715 return empty($user) ? false : array( 716 'user' => $user, 717 'password' => $password 718 ); 719 } 720 721 public function authenticate() { 722 if (!isset($_SERVER['HTTP_AUTHORIZATION']) && $this->hasAuthentication()) { 723 $authentication = $this->hasAuthentication(); 724 $_SERVER['HTTP_AUTHORIZATION'] = 'Basic ' . base64_encode($authentication['user'] . ':' . $authentication['password']); 725 $this->debug->message("Re-authenticating with default user from configuration", $authentication['user'], 3); 726 return auth_setup(); 727 } 728 729 return false; 730 } 731 732 /** 733 * Check the secret CSRF token, regardless of the current authorization 734 * 735 * @param null|string $token security token 736 * @param null|boolean $softfail if a message is to be thrown. 737 * @return bool success if the token matched 738 */ 739 function checkSecurityToken($token = null, $softfail = true) { 740 /** @var Input $INPUT */ 741 $secToken = $this->getSecurityToken(); 742 if ( empty( $secToken) && empty ( $token ) ) return false; 743 if($secToken != $token) { 744 if ( !$softfail ) msg('Security Token did not match. Possible CSRF attack.', -1); 745 return false; 746 } 747 return true; 748 } 749 750 /** 751 * Return a secret token to be used for CSRF attack prevention 752 * This is known to be flawed by default 753 * 754 * @author Andreas Gohr <andi@splitbrain.org> 755 * @link http://en.wikipedia.org/wiki/Cross-site_request_forgery 756 * @link http://christ1an.blogspot.com/2007/04/preventing-csrf-efficiently.html 757 * @link https://github.com/splitbrain/dokuwiki/issues/1883 758 * 759 * @return string 760 */ 761 function getSecurityToken() { 762 /** @var Input $INPUT */ 763 global $INPUT; 764 return PassHash::hmac('md5', session_id().'siteexport', 'siteexport_salt'.auth_cookiesalt()); 765 } 766} 767