with your question(s) and I'll
provide explanations (and add comments) that answer them.
*/
error_reporting(E_ALL ^ E_NOTICE);
StopWatch('PmWiki');
if (ini_get('register_globals'))
foreach($_REQUEST as $k=>$v) { unset(${$k}); }
$UnsafeGlobals = array_keys($GLOBALS); $GCount=0; $FmtV=array();
SDV($FarmD,dirname(__FILE__));
SDV($WorkDir,'wiki.d');
define('PmWiki',1);
@include_once("$FarmD/scripts/version.php");
$GroupPattern = '[[:upper:]][\\w]*(?:-\\w+)*';
$NamePattern = '[[:upper:]\\d][\\w]*(?:-\\w+)*';
$WikiWordPattern = '[[:upper:]][[:alnum:]]*(?:[[:upper:]][[:lower:]0-9]|[[:lower:]0-9][[:upper:]])[[:alnum:]]*';
$WikiDir = new PageStore('wiki.d/$FullName');
$WikiLibDirs = array(&$WikiDir,new PageStore('$FarmD/wikilib.d/$FullName'));
$InterMapFiles = array("$FarmD/scripts/intermap.txt",
"$FarmD/local/farmmap.txt", 'local/localmap.txt');
$Newline = "\263"; # deprecated, 2.0.0
$KeepToken = "\235\235";
$K0=array('='=>'','@'=>''); $K1=array('='=>'','@'=>'');
$Now=time();
define('READPAGE_CURRENT', $Now+604800);
$TimeFmt = '%B %d, %Y, at %I:%M %p';
$MessagesFmt = array();
$BlockMessageFmt = "
','','
',0), 'indent' => array("','','',0), 'table' => array("
mkdir $parent/$dir\n chmod 777 $parent/$dirThen, reload this page."; $safemode = ini_get('safe_mode'); if (!$safemode) $msg .= "
chmod 2777 $parenton your server and following this link. Afterwards you can restore the permissions to their current setting by executing
chmod $perms $parent."; Abort($msg); } ## fixperms attempts to correct permissions on a file or directory ## so that both PmWiki and the account (current dir) owner can manipulate it function fixperms($fname, $add = 0) { clearstatcache(); if (!file_exists($fname)) Abort('no such file'); $bp = 0; if (fileowner($fname)!=fileowner('.')) $bp = (is_dir($fname)) ? 007 : 006; if (filegroup($fname)==filegroup('.')) $bp <<= 3; $bp |= $add; if ($bp && (fileperms($fname) & $bp) != $bp) @chmod($fname,fileperms($fname)|$bp); } ## MakePageName is used to convert a string into a valid pagename. ## If no group is supplied, then it uses $PagePathFmt to look ## for the page in other groups, or else uses the group of the ## pagename passed as an argument. function MakePageName($basepage,$x) { global $MakePageNameFunction, $PageNameChars, $PagePathFmt, $MakePageNamePatterns; if (@$MakePageNameFunction) return $MakePageNameFunction($basepage,$x); SDV($PageNameChars,'-[:alnum:]'); SDV($MakePageNamePatterns, array( "/'/" => '', # strip single-quotes "/[^$PageNameChars]+/" => ' ', # convert everything else to space "/((^|[^-\\w])\\w)/e" => "strtoupper('$1')", "/ /" => '')); if (!preg_match('/(?:([^.\\/]+)[.\\/])?([^.\\/]+)$/',$x,$m)) return ''; $name = preg_replace(array_keys($MakePageNamePatterns), array_values($MakePageNamePatterns), $m[2]); if ($m[1]) { $group = preg_replace(array_keys($MakePageNamePatterns), array_values($MakePageNamePatterns), $m[1]); return "$group.$name"; } foreach((array)$PagePathFmt as $pg) { $pn = FmtPageName(str_replace('$1',$name,$pg),$basepage); if (PageExists($pn)) return $pn; } $group=preg_replace('/[\\/.].*$/','',$basepage); return "$group.$name"; } ## PCache caches basic information about a page and its attributes-- ## usually everything except page text and page history. This makes ## for quicker access to certain values in FmtPageName below. function PCache($pagename,$page) { global $PCache; foreach($page as $k=>$v) if ($k!='text' && strpos($k,':')===false) $PCache[$pagename][$k]=$v; } ## FmtPageName handles $[internationalization] and $Variable ## substitutions in strings based on the $pagename argument. function FmtPageName($fmt,$pagename) { # Perform $-substitutions on $fmt relative to page given by $pagename global $GroupPattern, $NamePattern, $EnablePathInfo, $ScriptUrl, $GCount, $UnsafeGlobals, $FmtV, $FmtP, $PCache, $AsSpacedFunction; if (strpos($fmt,'$')===false) return $fmt; $fmt = preg_replace('/\\$([A-Z]\\w*Fmt)\\b/e','$GLOBALS[\'$1\']',$fmt); $fmt = preg_replace('/\\$\\[(?>([^\\]]+))\\]/e',"XL(PSS('$1'))",$fmt); $match = array('','$Group','$Name'); if (preg_match("/^($GroupPattern)[\\/.]($NamePattern)\$/", $pagename, $m)) $match = $m; $fmt = preg_replace(array_keys($FmtP),array_values($FmtP),$fmt); $fmt = preg_replace('!\\$ScriptUrl/([^?#\'"\\s<>]+)!e', (@$EnablePathInfo) ? "'$ScriptUrl/'.PUE('$1')" : "'$ScriptUrl?n='.str_replace('/','.',PUE('$1'))", $fmt); if (strpos($fmt,'$')===false) return $fmt; static $g; if ($GCount != count($GLOBALS)+count($FmtV)) { $g = array(); foreach($GLOBALS as $n=>$v) { if (is_array($v) || is_object($v) || isset($FmtV["\$$n"]) || in_array($n,$UnsafeGlobals)) continue; $g["\$$n"] = $v; } $GCount = count($GLOBALS)+count($FmtV); krsort($g); reset($g); } $fmt = str_replace(array_keys($g),array_values($g),$fmt); $fmt = str_replace(array_keys($FmtV),array_values($FmtV),$fmt); return $fmt; } ## The XL functions provide translation tables for $[i18n] strings ## in FmtPageName(). function XL($key) { global $XL,$XLLangs; foreach($XLLangs as $l) if (isset($XL[$l][$key])) return $XL[$l][$key]; return $key; } function XLSDV($lang,$a) { global $XL; foreach($a as $k=>$v) { if (!isset($XL[$lang][$k])) $XL[$lang][$k]=$v; } } function XLPage($lang,$p) { global $TimeFmt,$XLLangs,$FarmD; $page = ReadPage($p, READPAGE_CURRENT); if (!$page) return; $text = preg_replace("/=>\\s*\n/",'=> ',@$page['text']); foreach(explode("\n",$text) as $l) if (preg_match('/^\\s*[\'"](.+?)[\'"]\\s*=>\\s*[\'"](.+)[\'"]/',$l,$match)) $xl[stripslashes($match[1])] = stripslashes($match[2]); if (isset($xl)) { if (@$xl['xlpage-i18n']) { $i18n = preg_replace('/[^-\\w]/','',$xl['xlpage-i18n']); include_once("$FarmD/scripts/xlpage-$i18n.php"); } if (@$xl['Locale']) setlocale(LC_ALL,$xl['Locale']); if (@$xl['TimeFmt']) $TimeFmt=$xl['TimeFmt']; array_unshift($XLLangs,$lang); XLSDV($lang,$xl); } } ## CmpPageAttr is used with uksort to order a page's elements with ## the latest items first. This can make some operations more efficient. function CmpPageAttr($a, $b) { @list($x, $agmt) = explode(':', $a); @list($x, $bgmt) = explode(':', $b); if ($agmt != $bgmt) return ($agmt==0 || $bgmt==0) ? $agmt - $bgmt : $bgmt - $agmt; return strcmp($a, $b); } ## class PageStore holds objects that store pages via the native ## filesystem. class PageStore { var $dirfmt; function PageStore($d='$WorkDir/$FullName') { $this->dirfmt=$d; } function pagefile($pagename) { global $FarmD; $dfmt = $this->dirfmt; if ($pagename > '') { $pagename = str_replace('/', '.', $pagename); if ($dfmt == 'wiki.d/$FullName') # optimizations for return "wiki.d/$pagename"; # standard locations if ($dfmt == '$FarmD/wikilib.d/$FullName') # return "$FarmD/wikilib.d/$pagename"; # if ($dfmt == 'wiki.d/$Group/$FullName') return preg_replace('/([^.]+).*/', 'wiki.d/$1/$0', $pagename); } return FmtPageName($dfmt, $pagename); } function read($pagename, $since=0) { $newline = ''; $urlencoded = false; $pagefile = $this->pagefile($pagename); if ($pagefile && ($fp=@fopen($pagefile, "r"))) { while (!feof($fp)) { $line = fgets($fp, 4096); while (substr($line, -1, 1) != "\n" && !feof($fp)) { $line .= fgets($fp, 4096); } $line = rtrim($line); if ($urlencoded) $line = urldecode(str_replace('+', '%2b', $line)); @list($k,$v) = explode('=', $line, 2); if (!$k) continue; if ($k == 'version') { $ordered = (strpos($v, 'ordered=1') !== false); $urlencoded = (strpos($v, 'urlencoded=1') !== false); if (strpos($v, 'pmwiki-0.')) $newline="\262"; } if ($k == 'newline') { $newline = $v; continue; } if ($since > 0 && preg_match('/:(\\d+)/', $k, $m) && $m[1] < $since) { if ($ordered) break; continue; } if ($newline) $v = str_replace($newline, "\n", $v); $page[$k] = $v; } fclose($fp); } return @$page; } function write($pagename,$page) { global $Now, $Version, $NewlineXXX; $page['name'] = $pagename; $page['time'] = $Now; $page['host'] = $_SERVER['REMOTE_ADDR']; $page['agent'] = @$_SERVER['HTTP_USER_AGENT']; $page['rev'] = @$page['rev']+1; unset($page['version']); unset($page['newline']); uksort($page, 'CmpPageAttr'); $s = false; $pagefile = $this->pagefile($pagename); $dir = dirname($pagefile); mkdirp($dir); if (!file_exists("$dir/.htaccess") && $fp = @fopen("$dir/.htaccess", "w")) { fwrite($fp, "Order Deny,Allow\nDeny from all\n"); fclose($fp); } if ($pagefile && ($fp=fopen("$pagefile,new","w"))) { $r0 = array('%', "\n", '<'); $r1 = array('%25', '%0a', '%3c'); $x = "version=$Version ordered=1 urlencoded=1\n"; if ($NewlineXXX) { $r1[1] = $NewlineXXX; $x .= "newline=$NewlineXXX\n"; } $s = true && fputs($fp, $x); $sz = strlen($x); foreach($page as $k=>$v) if ($k > '' && $k{0} != '=') { $x = str_replace($r0, $r1, "$k=$v") . "\n"; $s = $s && fputs($fp, $x); $sz += strlen($x); } $s = fclose($fp) && $s; $s = $s && (filesize("$pagefile,new") > $sz * 0.95); if (file_exists($pagefile)) $s = $s && unlink($pagefile); $s = $s && rename("$pagefile,new", $pagefile); } $s && fixperms($pagefile); if (!$s) Abort("Cannot write page to $pagename ($pagefile)...changes not saved"); PCache($pagename, $page); } function exists($pagename) { $pagefile = $this->pagefile($pagename); return ($pagefile && file_exists($pagefile)); } function delete($pagename) { global $Now; $pagefile = $this->pagefile($pagename); @rename($pagefile,"$pagefile,del-$Now"); } function ls($pats=NULL) { global $GroupPattern, $NamePattern; $pats=(array)$pats; array_unshift($pats, "/^$GroupPattern\.$NamePattern$/"); $dir = $this->pagefile(''); $dirlist = array(preg_replace('!/*[^/]*\\$.*$!','',$dir)); $out = array(); while (count($dirlist)>0) { $dir = array_shift($dirlist); $dfp = opendir($dir); if (!$dfp) { continue; } while ( ($pagefile = readdir($dfp)) !== false) { if ($pagefile{0} == '.') continue; if (is_dir("$dir/$pagefile")) { array_push($dirlist,"$dir/$pagefile"); continue; } if (@$seen[$pagefile]++) continue; foreach($pats as $p) { if ($p{0} == '!') { if (preg_match($p,$pagefile)) continue 2; } else if (!preg_match($p,$pagefile)) continue 2; } $out[] = $pagefile; } closedir($dfp); } return $out; } } function ReadPage($pagename, $since=0) { # read a page from the appropriate directories given by $WikiReadDirsFmt. global $WikiLibDirs,$Now; foreach ($WikiLibDirs as $dir) { $page = $dir->read($pagename, $since); if ($page) break; } if (@!$page['time']) $page['time']=$Now; return $page; } function WritePage($pagename,$page) { global $WikiDir,$LastModFile; $WikiDir->write($pagename,$page); if ($LastModFile && !@touch($LastModFile)) { unlink($LastModFile); touch($LastModFile); fixperms($LastModFile); } } function PageExists($pagename) { global $WikiLibDirs; static $pe; if (!isset($pe[$pagename])) { $pe[$pagename] = false; foreach((array)$WikiLibDirs as $dir) if ($dir->exists($pagename)) { $pe[$pagename] = true; break; } } return $pe[$pagename]; } function ListPages($pat=NULL) { global $WikiLibDirs; foreach((array)$WikiLibDirs as $dir) $out = array_unique(array_merge($dir->ls($pat),(array)@$out)); return $out; } function RetrieveAuthPage($pagename, $level, $authprompt=true, $since=0) { global $AuthFunction; SDV($AuthFunction,'PmWikiAuth'); if (!function_exists($AuthFunction)) return ReadPage($pagename, $since); return $AuthFunction($pagename, $level, $authprompt, $since); } function Abort($msg) { # exit pmwiki with an abort message echo "
$msg
We are sorry for any inconvenience.
"; exit; } function Redirect($pagename,$urlfmt='$PageUrl') { # redirect the browser to $pagename global $EnableRedirect,$RedirectDelay; SDV($RedirectDelay,0); clearstatcache(); #if (!PageExists($pagename)) $pagename=$DefaultPage; $pageurl = FmtPageName($urlfmt,$pagename); if (IsEnabled($EnableRedirect,1) && (!isset($_REQUEST['redirect']) || $_REQUEST['redirect'])) { header("Location: $pageurl"); header("Content-type: text/html"); echo "($[redirected from] \$FullName)
\$HTMLVSpace\n"); if (isset($page['text'])) $text=$page['text']; else $text = FmtPageName($DefaultPageTextFmt,$pagename); if (@!$_GET['from']) { $PageRedirectFmt = ''; if (preg_match('/\\(:redirect\\s+(.+?):\\)/',$text,$match)) { $rname = MakePageName($pagename,$match[1]); if (PageExists($rname)) Redirect($rname,"\$PageUrl?from=$pagename"); } } else $PageRedirectFmt=FmtPageName($PageRedirectFmt,$_GET['from']); $text = '(:groupheader:)'.@$text.'(:groupfooter:)'; $FmtV['$PageText'] = MarkupToHTML($pagename,$text); SDV($HandleBrowseFmt,array(&$PageStartFmt,&$PageRedirectFmt,'$PageText', &$PageEndFmt)); PrintFmt($pagename,$HandleBrowseFmt); } # EditTemplate allows a site administrator to pre-populate new pages # with the contents of another page. function EditTemplate($pagename, &$page, &$new) { global $EditTemplatesFmt; if (@$new['text'] > '') return; if (@$_REQUEST['template'] && PageExists($_REQUEST['template'])) { $p = RetrieveAuthPage($_REQUEST['template'], 'read', false, READPAGE_CURRENT); if ($p['text'] > '') $new['text'] = $p['text']; return; } foreach((array)$EditTemplatesFmt as $t) { $p = RetrieveAuthPage(FmtPageName($t,$pagename), 'read', false, READPAGE_CURRENT); if (@$p['text'] > '') { $new['text'] = $p['text']; return; } } } # RestorePage handles returning to the version of text as of # the version given by $restore or $_REQUEST['restore']. function RestorePage($pagename,&$page,&$new,$restore=NULL) { if (is_null($restore)) $restore=@$_REQUEST['restore']; if (!$restore) return; $t = $page['text']; $nl = (substr($t,-1)=="\n"); $t = explode("\n",$t); if ($nl) array_pop($t); krsort($page); reset($page); foreach($page as $k=>$v) { if ($k<$restore) break; if (strncmp($k, 'diff:', 5) != 0) continue; foreach(explode("\n",$v) as $x) { if (preg_match('/^(\\d+)(,(\\d+))?([adc])(\\d+)/',$x,$match)) { $a1 = $a2 = $match[1]; if ($match[3]) $a2=$match[3]; $b1 = $match[5]; if ($match[4]=='d') array_splice($t,$b1,$a2-$a1+1); if ($match[4]=='c') array_splice($t,$b1-1,$a2-$a1+1); continue; } if (strncmp($x,'< ',2) == 0) { $nlflag=true; continue; } if (preg_match('/^> (.*)$/',$x,$match)) { $nlflag=false; array_splice($t,$b1-1,0,$match[1]); $b1++; } if ($x=='\\ No newline at end of file') $nl=$nlflag; } } if ($nl) $t[]=''; $new['text']=implode("\n",$t); return $new['text']; } ## ReplaceOnSave performs any text replacements (held in $ROSPatterns) ## on the new text prior to saving the page. function ReplaceOnSave($pagename,&$page,&$new) { global $EnablePost, $ROSPatterns; if (!$EnablePost) return; foreach((array)$ROSPatterns as $pat=>$repfmt) $new['text'] = preg_replace($pat,FmtPageName($repfmt,$pagename),$new['text']); } function SaveAttributes($pagename,&$page,&$new) { global $EnablePost, $LinkTargets; if (!$EnablePost) return; unset($new['title']); $text = preg_replace('/\\[([=@]).*?\\1\\]/s',' ',$new['text']); if (preg_match('/\\(:title\\s(.+?):\\)/',$text,$match)) $new['title'] = $match[1]; MarkupToHTML($pagename,preg_replace('/\\(:(.*?):\\)/s',' ',$text)); $new['targets'] = implode(',',array_keys((array)$LinkTargets)); } function PostPage($pagename, &$page, &$new) { global $DiffKeepDays, $DiffFunction, $DeleteKeyPattern, $EnablePost, $Now, $Author, $WikiDir, $IsPagePosted, $NewlineXXX; SDV($DiffKeepDays,3650); SDV($DeleteKeyPattern,"^\\s*delete\\s*$"); $IsPagePosted = false; if ($EnablePost) { if (@$NewlineXXX) $new['text']=str_replace($NewlineXXX,"\n",$new['text']); if ($new['text']==@$page['text']) { $IsPagePosted=true; return; } $new["author"]=@$Author; $new["author:$Now"] = @$Author; $new["host:$Now"] = $_SERVER['REMOTE_ADDR']; $diffclass = preg_replace('/\\W/','',@$_POST['diffclass']); if ($page["time"]>0 && function_exists(@$DiffFunction)) $new["diff:$Now:{$page['time']}:$diffclass"] = $DiffFunction($new['text'],@$page['text']); $keepgmt = $Now-$DiffKeepDays * 86400; $keys = array_keys($new); foreach($keys as $k) if (preg_match("/^\\w+:(\\d+)/",$k,$match) && $match[1]<$keepgmt) unset($new[$k]); if (preg_match("/$DeleteKeyPattern/",$new['text'])) $WikiDir->delete($pagename); else WritePage($pagename,$new); $IsPagePosted = true; } } function PostRecentChanges($pagename,&$page,&$new) { global $IsPagePosted, $RecentChangesFmt, $RCDelimPattern, $RCLinesMax; if (!$IsPagePosted) return; foreach($RecentChangesFmt as $rcfmt=>$pgfmt) { $rcname = FmtPageName($rcfmt,$pagename); if (!$rcname) continue; $pgtext = FmtPageName($pgfmt,$pagename); if (!$pgtext) continue; if (@$seen[$rcname]++) continue; $rcpage = ReadPage($rcname); $rcelim = preg_quote(preg_replace("/$RCDelimPattern.*$/",' ',$pgtext),'/'); $rcpage['text'] = preg_replace("/[^\n]*$rcelim.*\n/","",@$rcpage['text']); if (!preg_match("/$RCDelimPattern/",$rcpage['text'])) $rcpage['text'] .= "$pgtext\n"; else $rcpage['text'] = preg_replace("/([^\n]*$RCDelimPattern.*\n)/", "$pgtext\n$1", $rcpage['text'], 1); if (@$RCLinesMax > 0) $rcpage['text'] = implode("\n", array_slice( explode("\n", $rcpage['text'], $RCLinesMax + 1), 0, $RCLinesMax)); WritePage($rcname, $rcpage); } } function PreviewPage($pagename,&$page,&$new) { global $IsPageSaved, $FmtV; if (!$IsPageSaved && @$_POST['preview']) { $text = '(:groupheader:)'.$new['text'].'(:groupfooter:)'; $FmtV['$PreviewText'] = MarkupToHTML($pagename,$text); } } function HandleEdit($pagename, $auth = 'edit') { global $IsPagePosted, $EditFields, $ChangeSummary, $EditFunctions, $EnablePost, $FmtV, $Now, $PageEditForm, $HandleEditFmt, $PageStartFmt, $PageEditFmt, $PageEndFmt; if (@$_POST['cancel']) { Redirect($pagename); return; } Lock(2); $IsPagePosted = false; $page = RetrieveAuthPage($pagename, $auth, true); if (!$page) Abort("?cannot edit $pagename"); PCache($pagename,$page); $new = $page; foreach((array)$EditFields as $k) if (isset($_POST[$k])) $new[$k]=str_replace("\r",'',stripmagic($_POST[$k])); if ($ChangeSummary) $new["csum:$Now"] = $ChangeSummary; $EnablePost &= (@$_POST['post'] || @$_POST['postedit']); foreach((array)$EditFunctions as $fn) $fn($pagename,$page,$new); Lock(0); if ($IsPagePosted && !@$_POST['postedit']) { Redirect($pagename); return; } $FmtV['$DiffClassMinor'] = (@$_POST['diffclass']=='minor') ? "checked='checked'" : ''; $FmtV['$EditText'] = str_replace('$','$',htmlspecialchars(@$new['text'],ENT_NOQUOTES)); $FmtV['$EditBaseTime'] = $Now; if (@$PageEditForm) { $form = ReadPage(FmtPageName($PageEditForm, $pagename), READPAGE_CURRENT); $FmtV['$EditForm'] = MarkupToHTML($pagename, $form['text']); } SDV($PageEditFmt, "$[EnterAttributes]