1<?php 2 3if (!defined('DOKU_PLUGIN')) die('meh'); 4require_once(DOKU_PLUGIN . 'siteexport/inc/settings.php'); 5require_once(DOKU_PLUGIN . 'siteexport/inc/debug.php'); 6 7class siteexport_functions extends DokuWiki_Plugin 8{ 9 public $debug = null; 10 public $settings = null; 11 12 public function __construct($init = true, $isAJAX = false) 13 { 14 if ($init) 15 { 16 $this->debug = new siteexport_debug(); 17 $this->debug->isAJAX = $isAJAX; 18 19 $this->settings = new settings_plugin_siteexport_settings($this); 20 $this->debug->message("Settings completed: zipFile", $this->settings->zipFile, 1); 21 } 22 } 23 24 public function getPluginName() 25 { 26 return 'siteexport'; 27 } 28 29 public function downloadURL() 30 { 31 $params = array('cache' => 'nocache', 'siteexport' => $this->settings->pattern); 32 33 if ($this->debug->debugLevel() < 5) { 34 // If debug, then debug! 35 $params['debug'] = $this->debug->debugLevel(); 36 } 37 38 return ml($this->settings->origZipFile, $params, true, '&'); 39 } 40 41 public function checkIfCacheFileExistsForFileWithPattern($file, $pattern) 42 { 43 if (!@file_exists($file)) 44 { 45 // If the cache File does not exist, move the newly created one over ... 46 $this->debug->message("'{$file}' does not exist. Checking original ZipFile", null, 3); 47 $newCacheFile = mediaFN($this->getSpecialExportFileName($this->settings->origZipFile, $pattern)); 48 49 if (!@file_exists($newCacheFile)) 50 { 51 $this->debug->message("The export must have gone wrong. The cached file does not exist.", array("pattern" => $pattern, "original File" => $this->settings->origZipFile, "expected cached file" => $newCacheFile), 3); 52 } 53 54 $status = io_rename($newCacheFile, $file); 55 $this->debug->message("had to move another original file over. Did it work? " . ($status ? 'Yes, it did.' : 'No, it did not.'), null, 2); 56 } else { 57 $this->debug->message("The file does exist!", $file, 2); 58 } 59 } 60 61 62 /** 63 * Returns an utf8 encoded Namespace for a Page and input Namespace 64 * @param $NS 65 * @param $PAGE 66 */ 67 function getNamespaceFromID($NS, &$PAGE) { 68 global $conf; 69 // Check current page - if its an NS add the startpage 70 $clean = true; 71 resolve_pageid(getNS($NS), $NS, $clean); 72 $NSa = explode(':', $NS); 73 if (!page_exists($NS) && array_pop($NSa) != strtolower($conf['start'])) { // Compare to lowercase since clean lowers it. 74 $NS .= ':' . $conf['start']; 75 resolve_pageid(getNS($NS), $NS, $clean); 76 } 77 78 $PAGE = noNS($NS); 79 $NS = getNS($NS); 80 81 return utf8_encodeFN(str_replace(':', '/', $NS)); 82 } 83 84 /** 85 * create a file name for the page 86 **/ 87 public function getSiteName($ID, $overrideRewrite = false) { 88 global $conf; 89 90 if (empty($ID)) return false; 91 92 // Remove extensions 93 if ($overrideRewrite) { 94 $ID = preg_replace("#\.(php|html)$#", '', $ID); 95 } 96 97 $url = $this->wl($this->cleanID($ID), null, true, null, null, $overrideRewrite); // this must be done with rewriting set to override 98 //$url = $this->wl($this->cleanID($ID), null, true); // this must be done with rewriting set to override 99 $uri = @parse_url($url); 100 if ($uri['path'][0] == '/') { 101 $uri['path'] = substr($uri['path'], 1); 102 } 103 104 return $this->shortenName($uri['path'] . '.' . $this->settings->fileType); 105 } 106 107 /** 108 * get the Title for the page 109 **/ 110 public function getSiteTitle($ID) { 111 if (useHeading('content') && $ID) { 112 $heading = 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 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 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 // $id = utf8_strtolower($id); // NO LowerCase for us! 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 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 303 * @param $PATTERN - 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 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 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 $append = ''; 382 383 if (is_array($value)) { 384 foreach (array_values($value) as $aValue) { // Array Handling 385 $URL .= (strstr($URL, '?') ? '&' : '?') . $key . "[]=$aValue"; 386 } 387 } else { 388 $append = "$key=$value"; 389 $URL .= empty($append) || strstr($URL, $append) ? '' : (strstr($URL, '?') ? '&' : '?') . $append; 390 } 391 392 } 393 } 394 } 395 396 /** 397 * Cleans the wiki variables and returns a rebuild URL that has the new variables at hand 398 * @param $data 399 */ 400 function prepare_POSTData($data) 401 { 402 $NS = !empty($data['ns']) ? $data['ns'] : $data['id']; 403 404 $this->removeWikiVariables($data); 405 $data['do'] = 'siteexport'; 406 $additionalKeys = ''; 407 408 ksort($data); 409 410 $this->debug->message("Prepared POST data:", $data, 1); 411 412 foreach ($data as $key => $value) { 413 414 if (!is_array($value)) { continue; } 415 $this->debug->message("Found inner Array:", $value, 1); 416 417 asort($value); 418 foreach ($value as $innerKey => $aValue) 419 { 420 if (is_numeric($innerKey)) 421 { 422 $innerKey = ''; 423 } 424 425 $additionalKeys .= "&$key" . "[$innerKey]=$aValue"; 426 } 427 428 unset($data[$key]); 429 } 430 431 return wl($NS, $data, true, '&') . $additionalKeys; 432 } 433 434 /** 435 * Parses a String into a $_REQUEST Like variant. You have to tell if a decode of the values is needed 436 * @param $inputArray 437 * @param $decode 438 */ 439 public function parseStringToRequestArray($inputArray, $decode=false) 440 { 441 global $plugin_controller; 442 443 $outputArray = $inputArray; 444 if ( !is_array($inputArray) ) 445 { 446 $intermediate = str_replace("&", "&", $inputArray); 447 448 $outputArray = array(); 449 foreach( explode("&", $intermediate) as $param ) { 450 list($key, $value) = explode("=", $param, 2); 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 . "$p/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 function removeWikiVariables(&$removeArray, $advanced = false, $isString = false) { 498 499 $removeArray = $this->parseStringToRequestArray($removeArray); 500 501 // 2010-08-23 - If there is still the media set, retain the id for e.g. detail.php 502 if (!isset($removeArray['media'])) { 503 unset($removeArray['id']); 504 } 505 506 unset($removeArray['do']); 507 unset($removeArray['ns']); 508 unset($removeArray['call']); 509 unset($removeArray['sectok']); 510 unset($removeArray['rndval']); 511 unset($removeArray['tseed']); 512 unset($removeArray['http_credentials']); 513 unset($removeArray['u']); 514 unset($removeArray['p']); 515 unset($removeArray['r']); 516 unset($removeArray['base']); 517 unset($removeArray['siteexport']); 518 unset($removeArray['DokuWiki']); 519 520 if ($removeArray['renderer'] == 'xhtml') { 521 $removeArray['do'] = 'export_' . $removeArray['renderer']; 522 unset($removeArray['renderer']); 523 } 524 525 // Keep custom options 526 if (is_array($removeArray['customoptionname']) && is_array($removeArray['customoptionvalue']) && count($removeArray['customoptionname']) == count($removeArray['customoptionvalue'])) 527 { 528 for ($index = 0; $index < count($removeArray['customoptionname']); $index++) 529 { 530 $removeArray[$removeArray['customoptionname'][$index]] = $removeArray['customoptionvalue'][$index]; 531 } 532 unset($removeArray['customoptionname']); 533 unset($removeArray['customoptionvalue']); 534 } 535 536 if ($advanced) { 537 if ($removeArray['renderer'] != 'xhtml' && !empty($removeArray['renderer'])) { 538 $removeArray['do'] = 'export_' . $removeArray['renderer']; 539 } 540 541 // 2010-08-25 - Need fakeMedia for some _detail cases with rewrite = 2 542 if (isset($removeArray['fakeMedia'])) { 543 unset($removeArray['media']); 544 unset($removeArray['fakeMedia']); 545 } 546 547 /* remove internal params */ 548 unset($removeArray['ens']); 549 unset($removeArray['renderer']); 550 unset($removeArray['site']); 551 unset($removeArray['namespace']); 552 unset($removeArray['exportbody']); 553 unset($removeArray['addParams']); 554 unset($removeArray['template']); 555 unset($removeArray['eclipseDocZip']); 556 unset($removeArray['useTocFile']); 557 unset($removeArray['JavaHelpDocZip']); 558 unset($removeArray['depth']); 559 unset($removeArray['depthType']); 560 unset($removeArray['startcounter']); 561 unset($removeArray['pattern']); 562 unset($removeArray['TOCMapWithoutTranslation']); 563 // unset($removeArray['disableCache']); 564 unset($removeArray['debug']); 565 } 566 567 if ($isString && is_array($removeArray)) { 568 $intermediate = $removeArray; 569 $removeArray = array(); 570 571 foreach ($intermediate as $key => $value) { 572 if (is_array($value)) { 573 foreach (array_values($value) as $aValue) { // Array Handling 574 $removeArray[] = $key . "[]=$aValue"; 575 } 576 } else { 577 $value = trim($value); 578 579 $removeArray[] = "$key" . (((empty($value) && intval($value) !== 0)) || $value == '' ? '' : "=$value"); // If the Value is empty, the Key must be preserved 580 } 581 } 582 583 //$removeArray = implode( ($this->settings->fileType == 'pdf' ? "&" : "&"), $removeArray); 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 - URL which will be made relative if needed 623 * @param baseURL - URL which is the reference to be made relative against 624 * @param string $replaceURL 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 while (count($replaceParts) > 0 && count($baseParts) > 0) { 654 655 if ($baseParts[0] == $replaceParts[0]) { 656 // Beginning is OK, so remove it. 657 array_shift($replaceParts); 658 array_shift($baseParts); 659 } else { 660 break; 661 } 662 663 } 664 665 $this->debug->message("Found URL '{$replaceURL}' that is relative to current page '{$baseURL}'.", array($replaceParts, $baseParts), 1); 666 667 // Remove everything that is identical 668 $replaceParts[] = $fileName; 669 670 // do the final link calculation 671 $finalLink = str_repeat('../', count($baseParts)) . implode('/', $replaceParts); 672 673 // find out if this is outside of our own export context, becond the baseURL 674 $offsiteTemplate = $this->getConf("offSiteLinkTemplate"); 675 $this->debug->message("Checking for offsite links", array( 676 "baseParts" => count($baseParts), 677 "originalBaseParts" => $originalBasePartsCount, 678 "ExistingPageID" => $existingPageID, 679 "offsiteTemplate" => $offsiteTemplate 680 ), 1); 681 if (count($baseParts) == $originalBasePartsCount && $existingPageID != null && !empty($offsiteTemplate)) { 682 683 $offsiteTemplate = str_replace('RAWID', $existingPageID, $offsiteTemplate); 684 685 $check = null; 686 $offsiteTemplate = str_replace('CONTEXTID', array_pop($this->getMapID($existingPageID, null, $check)), $offsiteTemplate); 687 $offsiteTemplate = str_replace('LINK', $finalLink, $offsiteTemplate); 688 689 $this->debug->message("Replacing finalLink '${finalLink}' with offsiteLink '${offsiteTemplate}'", null, 1); 690 $finalLink = $offsiteTemplate; 691 } 692 693 return $finalLink; 694 } 695 696 function mapIDWithAnchor(&$n, $key, $postfix) 697 { 698 if (empty($postfix)) return; 699 $n .= '-' . $postfix; 700 } 701 702 function getMapID($elemID, $postfix, &$check) 703 { 704 $meta = p_get_metadata($elemID, 'context', true); 705 706 if (empty($meta['id'])) { 707 $title = empty($meta['title']) ? $this->getSiteTitle($elemID) : $meta['title']; 708 $meta['id'] = sectionID($this->cleanId(strtolower($title)), $check); 709 } 710 711 $mapID = explode('|', $meta['id']); 712 array_walk($mapID, array($this, 'mapIDWithAnchor'), $postfix); 713 714 return $mapID; 715 } 716 717 public function hasAuthentication() { 718 $user = $this->getConf('defaultAuthenticationUser'); 719 $password = $this->getConf('defaultAuthenticationPassword'); 720 return empty($user) ? false : array( 721 'user' => $user, 722 'password' => $password 723 ); 724 } 725 726 public function authenticate() { 727 if (!isset($_SERVER['HTTP_AUTHORIZATION']) && $this->hasAuthentication()) { 728 $authentication = $this->hasAuthentication(); 729 $_SERVER['HTTP_AUTHORIZATION'] = 'Basic ' . base64_encode($authentication['user'] . ':' . $authentication['password']); 730 $this->debug->message("Re-authenticating with default user from configuration", $authentication['user'], 3); 731 return auth_setup(); 732 } 733 734 return false; 735 } 736 737 /** 738 * Check the secret CSRF token, regardless of the current authorization 739 * 740 * @param null|string $token security token 741 * @param null|boolean $softfail if a message is to be thrown. 742 * @return bool success if the token matched 743 */ 744 function checkSecurityToken($token = null, $softfail = true) { 745 /** @var Input $INPUT */ 746 $secToken = $this->getSecurityToken(); 747 if ( empty( $secToken) && empty ( $token ) ) return false; 748 if($secToken != $token) { 749 if ( !$softfail ) msg('Security Token did not match. Possible CSRF attack.', -1); 750 return false; 751 } 752 return true; 753 } 754 755 /** 756 * Return a secret token to be used for CSRF attack prevention 757 * This is known to be flawed by default 758 * 759 * @author Andreas Gohr <andi@splitbrain.org> 760 * @link http://en.wikipedia.org/wiki/Cross-site_request_forgery 761 * @link http://christ1an.blogspot.com/2007/04/preventing-csrf-efficiently.html 762 * @link https://github.com/splitbrain/dokuwiki/issues/1883 763 * 764 * @return string 765 */ 766 function getSecurityToken() { 767 /** @var Input $INPUT */ 768 global $INPUT; 769 return PassHash::hmac('md5', session_id().'siteexport', 'siteexport_salt'.auth_cookiesalt()); 770 } 771} 772