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