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 $intermediate = str_replace("&", "&", $inputArray); 444 445 $outputArray = array(); 446 foreach( explode("&", $intermediate) as $param ) { 447 list($key, $value) = array_pad( explode("=", $param, 2), 2, null ); 448 449 // This is needed if we do want to calculate $_REQUEST for a non HTTP-Request 450 if ( $decode) 451 { 452 $value = urldecode($value); 453 } 454 455 if ( empty($key) ) { continue; } // Don't check on Value, because there may be only the key that should be preserved 456 457 if ( substr($key, -2) == '[]' ) { 458 $key = substr($key, 0, -2); 459 if ( !is_array($outputArray[$key]) ) { 460 $outputArray[$key] = array(); 461 } 462 463 array_push($outputArray[$key], $value); // Array Handling 464 } else { 465 $outputArray[$key] = $value; 466 } 467 } 468 } 469 470 if (!empty($outputArray['diPlu'])) { 471 472 $allPlugins = array(); 473 foreach ($plugin_controller->getList(null, true) as $plugin) { 474 // check for CSS or JS 475 if (!file_exists(DOKU_PLUGIN . $plugin . "/script.js") && !file_exists(DOKU_PLUGIN . $plugin . "/style.css")) { continue; } 476 $allPlugins[] = $plugin; 477 } 478 479 if (count($outputArray['diPlu']) > (count($allPlugins)/2)) { 480 $outputArray['diInv'] = 1; 481 $outputArray['diPlu'] = array_diff($allPlugins, $outputArray['diPlu']); 482 } 483 } 484 485 return $outputArray; 486 } 487 488 /** 489 * Remove certain fields from the list. 490 * @param $removeArray 491 * @param $advanced 492 * @param $isString 493 */ 494 public function removeWikiVariables(&$removeArray, $advanced = false, $isString = false) { 495 496 $removeArray = $this->parseStringToRequestArray($removeArray); 497 $removeKeys = array(); 498 499 // 2010-08-23 - If there is still the media set, retain the id for e.g. detail.php 500 if (!isset($removeArray['media'])) { 501 $removeKeys[] = 'id'; 502 } 503 504 unset($removeArray['do']); 505 $removeKeys[] = 'ns'; 506 $removeKeys[] = 'call'; 507 $removeKeys[] = 'sectok'; 508 $removeKeys[] = 'rndval'; 509 $removeKeys[] = 'tseed'; 510 $removeKeys[] = 'http_credentials'; 511 $removeKeys[] = 'u'; 512 $removeKeys[] = 'p'; 513 $removeKeys[] = 'r'; 514 $removeKeys[] = 'base'; 515 $removeKeys[] = 'siteexport'; 516 $removeKeys[] = 'DokuWiki'; 517 518 if ( array_key_exists( 'renderer', $removeArray ) && $removeArray['renderer'] == 'xhtml') { 519 $removeArray['do'] = 'export_' . $removeArray['renderer']; 520 $removeKeys[] = 'renderer'; 521 } 522 523 // Keep custom options 524 if ( array_key_exists( 'customoptionname', $removeArray ) && is_array($removeArray['customoptionname']) && is_array($removeArray['customoptionvalue']) && count($removeArray['customoptionname']) == count($removeArray['customoptionvalue'])) 525 { 526 for ($index = count($removeArray['customoptionname']); $index >= 0; $index--) 527 { 528 $removeArray[$removeArray['customoptionname'][$index]] = $removeArray['customoptionvalue'][$index]; 529 } 530 $removeKeys[] = 'customoptionname'; 531 $removeKeys[] = 'customoptionvalue'; 532 } 533 534 if ($advanced) { 535 if ( array_key_exists( 'renderer', $removeArray ) && $removeArray['renderer'] != 'xhtml' && !empty($removeArray['renderer'])) { 536 $removeArray['do'] = 'export_' . $removeArray['renderer']; 537 } 538 539 // 2010-08-25 - Need fakeMedia for some _detail cases with rewrite = 2 540 if (isset($removeArray['fakeMedia'])) { 541 $removeKeys[] = 'media'; 542 $removeKeys[] = 'fakeMedia'; 543 } 544 545 /* remove internal params */ 546 $removeKeys[] = 'ens'; 547 $removeKeys[] = 'renderer'; 548 $removeKeys[] = 'site'; 549 $removeKeys[] = 'namespace'; 550 $removeKeys[] = 'exportbody'; 551 $removeKeys[] = 'addParams'; 552 $removeKeys[] = 'template'; 553 $removeKeys[] = 'eclipseDocZip'; 554 $removeKeys[] = 'useTocFile'; 555 $removeKeys[] = 'JavaHelpDocZip'; 556 $removeKeys[] = 'depth'; 557 $removeKeys[] = 'depthType'; 558 $removeKeys[] = 'startcounter'; 559 $removeKeys[] = 'pattern'; 560 $removeKeys[] = 'TOCMapWithoutTranslation'; 561 562 $removeKeys[] = 'debug'; 563 } 564 565 foreach($removeKeys as $key) { 566 unset($removeArray[$key]); 567 } 568 569 if ($isString && is_array($removeArray)) { 570 $intermediate = $removeArray; 571 $removeArray = array(); 572 573 foreach ($intermediate as $key => $value) { 574 if (is_array($value)) { 575 foreach (array_values($value) as $aValue) { // Array Handling 576 $removeArray[] = $key . "[]=$aValue"; 577 } 578 } else { 579 $value = trim($value); 580 581 $removeArray[] = "$key" . (((empty($value) && intval($value) !== 0)) || $value == '' ? '' : "=$value"); // If the Value is empty, the Key must be preserved 582 } 583 } 584 585 $removeArray = implode("&", $removeArray); // The & made problems with the HTTPClient / Apache. It should not be a problem to have & 586 } 587 } 588 589 /** 590 * returns a hashed name for the parameters 591 * @param $parameters 592 */ 593 public function hashNameForParameters($parameters) 594 { 595 return md5($parameters); 596 } 597 598 /** 599 * Takes an URL and transforms it into the path+query part 600 * Used several times, e.g. for genering the hash for the cache file 601 * @param string $url 602 */ 603 public function urlToPathAndParams($url) 604 { 605 $query = parse_url($url, PHP_URL_QUERY); 606 $path = preg_replace(":^" . DOKU_REL . ":", "", parse_url($url, PHP_URL_PATH)); 607 return "{$path}?{$query}"; 608 } 609 610 /** 611 * Transforms an $_REQUEST into a Hash that can be used for cron and cache file 612 * @param $request 613 */ 614 public function requestParametersToCacheHash($request) 615 { 616 $params = $this->urlToPathAndParams($this->prepare_POSTData($request)); 617 $this->debug->message("Calculated the following Cache Hash URL: ", $params, 2); 618 return $this->hashNameForParameters($params); 619 } 620 621 /** 622 * Check a replaceURL against a baseURL - and make the replaceURL relative against it 623 * @param replaceURL String URL which will be made relative if needed 624 * @param baseURL String URL which is the reference to be made relative against 625 * @param existingPageID Array 626 */ 627 public function getRelativeURL($replaceURL, $baseURL, $existingPageID = null) 628 { 629 // Base is always absolute without anything at the beginning 630 if (preg_match("#^(\.\./)+#", $baseURL)) { 631 $this->debug->message("The baseURL was not absolute.", $baseURL, 1); 632 return $replaceURL; 633 } 634 635 $origReplaceURL = $replaceURL; 636 $replaceURL = preg_replace("#^(\.\./)+#", '', $replaceURL); 637 638 // Remove ../ at beginning to get the absolute path 639 if ($replaceURL == $origReplaceURL) { 640 $this->debug->message("The replaceURL was already absolute.", $replaceURL, 1); 641 return $replaceURL; 642 } 643 644 $replaceParts = explode('/', $replaceURL); 645 $fileName = array_pop($replaceParts); // Get file 646 647 $baseParts = explode('/', $baseURL); 648 array_pop($baseParts); // Remove file. We only need the path to this location. 649 650 $this->debug->message("State before kicking.", array($replaceParts, $baseParts), 1); 651 652 // Kick all ../ 653 $originalBasePartsCount = count($baseParts); 654 $didKickSomeParts = 0; // true means, that some parts of the base URL were identical 655 while (count($replaceParts) > 0 && count($baseParts) > 0) { 656 657 if ($baseParts[0] == $replaceParts[0]) { 658 // Beginning is OK, so remove it. 659 array_shift($replaceParts); 660 array_shift($baseParts); 661 $didKickSomeParts++; 662 } else { 663 break; 664 } 665 666 } 667 668 $this->debug->message("Found URL '{$replaceURL}' that is relative to current page '{$baseURL}'.", array($replaceParts, $baseParts), 1); 669 670 // Remove everything that is identical 671 $replaceParts[] = $fileName; 672 673 // do the final link calculation 674 $finalLink = str_repeat('../', count($baseParts)) . implode('/', $replaceParts); 675 676 // Means nothing was kicked, so other plugin 677 $isExternalPage = count($baseParts) == $originalBasePartsCount; 678 679 // the new page is in the same plugin, with a different subcontext and same language 680 $isExternalPage = $isExternalPage || ($didKickSomeParts == 1 && $baseParts[0] != $replaceParts[0] && $baseParts[1] == $replaceParts[1] ); 681 682 // find out if this is outside of our own export context, beyond the baseURL 683 $offsiteTemplate = $this->getConf("offSiteLinkTemplate"); 684 $this->debug->message("Checking for offsite links", array( 685 "baseParts" => count($baseParts), 686 "originalBaseParts" => $originalBasePartsCount, 687 "ExistingPageID" => $existingPageID, 688 "finalLink" => $finalLink, 689 "offsiteTemplate" => $offsiteTemplate, 690 "isExternalPage" => $isExternalPage, 691 "didKickSomeParts" => $didKickSomeParts 692 693 ), 1); 694 695 if ( $isExternalPage && $existingPageID != null && !empty($offsiteTemplate)) { 696 697 $offsiteTemplate = str_replace('RAWID', $existingPageID, $offsiteTemplate); 698 699 $check = null; 700 $mapID = $this->getMapID($existingPageID, null, $check); 701 $offsiteTemplate = str_replace('CONTEXTID', array_pop($mapID), $offsiteTemplate); 702 $offsiteTemplate = str_replace('LINK', $finalLink, $offsiteTemplate); 703 704 $this->debug->message("Replacing finalLink '${finalLink}' with offsiteLink '${offsiteTemplate}'", null, 1); 705 $finalLink = $offsiteTemplate; 706 } 707 708 return $finalLink; 709 } 710 711 public function mapIDWithAnchor(&$n, $key, $postfix) 712 { 713 if (empty($postfix)) return; 714 $n .= '-' . $postfix; 715 } 716 717 public function getMapID($elemID, $postfix, &$check) 718 { 719 $meta = p_get_metadata($elemID, 'context', true); 720 721 if (empty($meta['id'])) { 722 $title = empty($meta['title']) ? $this->getSiteTitle($elemID) : $meta['title']; 723 $meta['id'] = sectionID($this->cleanId($title), $check); 724 } 725 726 $mapID = explode('|', $meta['id']); 727 array_walk($mapID, array($this, 'mapIDWithAnchor'), $postfix); 728 729 return $mapID; 730 } 731 732 public function hasAuthentication() { 733 $user = $this->getConf('defaultAuthenticationUser'); 734 $password = $this->getConf('defaultAuthenticationPassword'); 735 return empty($user) ? false : array( 736 'user' => $user, 737 'password' => $password 738 ); 739 } 740 741 public function authenticate() { 742 if (!isset($_SERVER['HTTP_AUTHORIZATION']) && $this->hasAuthentication()) { 743 $authentication = $this->hasAuthentication(); 744 $_SERVER['HTTP_AUTHORIZATION'] = 'Basic ' . base64_encode($authentication['user'] . ':' . $authentication['password']); 745 $this->debug->message("Re-authenticating with default user from configuration", $authentication['user'], 3); 746 return auth_setup(); 747 } 748 749 return false; 750 } 751 752 /** 753 * Check the secret CSRF token, regardless of the current authorization 754 * 755 * @param null|string $token security token 756 * @param null|boolean $softfail if a message is to be thrown. 757 * @return bool success if the token matched 758 */ 759 public function checkSecurityToken($token = null, $softfail = true) { 760 /** @var Input $INPUT */ 761 $secToken = $this->getSecurityToken(); 762 if ( empty( $secToken) && empty ( $token ) ) return false; 763 if($secToken != $token) { 764 if ( $softfail !== true ) msg('Security Token did not match. Possible CSRF attack.', -1); 765 return false; 766 } 767 return true; 768 } 769 770 /** 771 * Return a secret token to be used for CSRF attack prevention 772 * This is known to be flawed by default 773 * 774 * @author Andreas Gohr <andi@splitbrain.org> 775 * @link http://en.wikipedia.org/wiki/Cross-site_request_forgery 776 * @link http://christ1an.blogspot.com/2007/04/preventing-csrf-efficiently.html 777 * @link https://github.com/splitbrain/dokuwiki/issues/1883 778 * 779 * @return string 780 */ 781 public function getSecurityToken() { 782 /** @var Input $INPUT */ 783 global $INPUT; 784 return PassHash::hmac('md5', session_id().'siteexport', 'siteexport_salt'.auth_cookiesalt()); 785 } 786} 787