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