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