with your question(s) and I'll provide explanations (and add comments) that answer them. */ error_reporting(E_ALL ^ E_NOTICE); StopWatch('PmWiki'); @ini_set('magic_quotes_runtime', 0); @ini_set('magic_quotes_sybase', 0); if (@ini_get('pcre.backtrack_limit') < 1000000) @ini_set('pcre.backtrack_limit', 1000000); if (ini_get('register_globals')) foreach($_REQUEST as $k=>$v) { if (preg_match('/^(GLOBALS|_SERVER|_GET|_POST|_COOKIE|_FILES|_ENV|_REQUEST|_SESSION|FarmD|WikiDir)$/i', $k)) exit(); ${$k}=''; unset(${$k}); } $UnsafeGlobals = array_keys($GLOBALS); $GCount=0; $FmtV=array(); SDV($FarmD,dirname(__FILE__)); SDV($WorkDir,'wiki.d'); define('PmWiki',1); if (preg_match('/\\w\\w:/', $FarmD)) exit(); @include_once("$FarmD/scripts/version.php"); $GroupPattern = '[[:upper:]][\\w]*(?:-\\w+)*'; $NamePattern = '[[:upper:]\\d][\\w]*(?:-\\w+)*'; $BlockPattern = 'form|div|table|t[rdh]|p|[uo]l|d[ltd]|h[1-6r]|pre|blockquote'; $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}')); $PageFileEncodeFunction = 'PUE'; # only used if $WikiDir->encodefilenames is set $PageFileDecodeFunction = 'urldecode'; $LocalDir = 'local'; $InterMapFiles = array("$FarmD/scripts/intermap.txt", "$FarmD/local/farmmap.txt", '$SiteGroup.InterMap', 'local/localmap.txt'); $Newline = "\263"; # deprecated, 2.0.0 $KeepToken = "\034\034"; $Now=time(); define('READPAGE_CURRENT', $Now+604800); $TimeFmt = '%B %d, %Y, at %I:%M %p'; $TimeISOFmt = '%Y-%m-%dT%H:%M:%S'; $TimeISOZFmt = '%Y-%m-%dT%H:%M:%SZ'; $MessagesFmt = array(); $BlockMessageFmt = "

$[This post has been blocked by the administrator]

"; $EditFields = array('text'); $EditFunctions = array('EditTemplate', 'RestorePage', 'ReplaceOnSave', 'SaveAttributes', 'PostPage', 'PostRecentChanges', 'AutoCreateTargets', 'PreviewPage'); $EnablePost = 1; $ChangeSummary = substr(preg_replace('/[\\x00-\\x1f]|=\\]/', '', stripmagic(@$_REQUEST['csum'])), 0, 100); $AsSpacedFunction = 'AsSpaced'; $SpaceWikiWords = 0; $RCDelimPattern = ' '; $RecentChangesFmt = array( '$SiteGroup.AllRecentChanges' => '* [[{$Group}.{$Name}]] . . . $CurrentTime $[by] $AuthorLink: [=$ChangeSummary=]', '$Group.RecentChanges' => '* [[{$Group}/{$Name}]] . . . $CurrentTime $[by] $AuthorLink: [=$ChangeSummary=]'); $UrlScheme = (@$_SERVER['HTTPS']=='on' || @$_SERVER['SERVER_PORT']==443) ? 'https' : 'http'; $ScriptUrl = $UrlScheme.'://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']; $PubDirUrl = preg_replace('#/[^/]*$#', '/pub', $ScriptUrl, 1); $HTMLVSpace = ""; $HTMLPNewline = ''; $MarkupFrame = array(); $MarkupFrameBase = array('cs' => array(), 'vs' => '', 'ref' => 0, 'closeall' => array(), 'is' => array(), 'escape' => 1); $WikiWordCountMax = 1000000; $WikiWordCount['PmWiki'] = 1; $TableRowIndexMax = 1; $UrlExcludeChars = '<>"{}|\\\\^`()[\\]\''; $QueryFragPattern = "[?#][^\\s$UrlExcludeChars]*"; $SuffixPattern = '(?:-?[[:alnum:]]+)*'; $LinkPageSelfFmt = "\$LinkText"; $LinkPageExistsFmt = "\$LinkText"; $LinkPageCreateFmt = "\$LinkText?"; $UrlLinkFmt = "\$LinkText"; umask(002); $CookiePrefix = ''; $SiteGroup = 'Site'; $SiteAdminGroup = 'SiteAdmin'; $DefaultGroup = 'Main'; $DefaultName = 'HomePage'; $GroupHeaderFmt = '(:include {$Group}.GroupHeader self=0 basepage={*$FullName}:)(:nl:)'; $GroupFooterFmt = '(:nl:)(:include {$Group}.GroupFooter self=0 basepage={*$FullName}:)'; $PagePathFmt = array('{$Group}.$1','$1.$1','$1.{$DefaultName}'); $PageAttributes = array( 'passwdread' => '$[Set new read password:]', 'passwdedit' => '$[Set new edit password:]', 'passwdattr' => '$[Set new attribute password:]'); $XLLangs = array('en'); if (preg_match('/^C$|\.UTF-?8/i',setlocale(LC_ALL,0))) setlocale(LC_ALL,'en_US'); $FmtP = array(); $FmtPV = array( # '$ScriptUrl' => 'PUE($ScriptUrl)', ## $ScriptUrl is special '$PageUrl' => 'PUE(($EnablePathInfo) ? "$ScriptUrl/$group/$name" : "$ScriptUrl?n=$group.$name")', '$FullName' => '"$group.$name"', '$Groupspaced' => '$AsSpacedFunction($group)', '$Namespaced' => '$AsSpacedFunction($name)', '$Group' => '$group', '$Name' => '$name', '$Titlespaced' => 'FmtPageTitle(@$page["title"], $name, 1)', '$Title' => 'FmtPageTitle(@$page["title"], $name, 0)', '$LastModifiedBy' => '@$page["author"]', '$LastModifiedHost' => '@$page["host"]', '$LastModified' => 'strftime($GLOBALS["TimeFmt"], $page["time"])', '$LastModifiedSummary' => '@$page["csum"]', '$LastModifiedTime' => '$page["time"]', '$Description' => '@$page["description"]', '$SiteGroup' => '$GLOBALS["SiteGroup"]', '$VersionNum' => '$GLOBALS["VersionNum"]', '$Version' => '$GLOBALS["Version"]', '$Author' => 'NoCache($GLOBALS["Author"])', '$AuthId' => 'NoCache($GLOBALS["AuthId"])', '$DefaultGroup' => '$GLOBALS["DefaultGroup"]', '$DefaultName' => '$GLOBALS["DefaultName"]', '$BaseName' => 'MakeBaseName($pn)', '$Action' => '$GLOBALS["action"]', '$PasswdRead' => 'PasswdVar($pn, "read")', '$PasswdEdit' => 'PasswdVar($pn, "edit")', '$PasswdAttr' => 'PasswdVar($pn, "attr")', ); $SaveProperties = array('title', 'description', 'keywords'); $PageTextVarPatterns = array( 'var:' => '/^(:*\\s*(\\w[-\\w]*)\\s*:[ \\t]?)(.*)($)/m', '(:var:...:)' => '/(\\(: *(\\w[-\\w]*) *:(?!\\))\\s?)(.*?)(:\\))/s' ); $WikiTitle = 'PmWiki'; $Charset = 'ISO-8859-1'; $HTTPHeaders = array( "Expires: Tue, 01 Jan 2002 00:00:00 GMT", "Cache-Control: no-store, no-cache, must-revalidate", "Content-type: text/html; charset=ISO-8859-1;"); $CacheActions = array('browse','diff','print'); $EnableHTMLCache = 0; $NoHTMLCache = 0; $HTMLDoctypeFmt = " \n"; $HTMLStylesFmt['pmwiki'] = " ul, ol, pre, dl, p { margin-top:0px; margin-bottom:0px; } code.escaped { white-space: nowrap; } .vspace { margin-top:1.33em; } .indent { margin-left:40px; } .outdent { margin-left:40px; text-indent:-40px; } a.createlinktext { text-decoration:none; border-bottom:1px dotted gray; } a.createlink { text-decoration:none; position:relative; top:-0.5em; font-weight:bold; font-size:smaller; border-bottom:none; } img { border:0px; } "; $HTMLHeaderFmt['styles'] = array( ""); $HTMLBodyFmt = "\n"; $HTMLStartFmt = array('headers:',&$HTMLDoctypeFmt,&$HTMLHeaderFmt, &$HTMLBodyFmt); $HTMLEndFmt = "\n\n"; $PageStartFmt = array(&$HTMLStartFmt,"\n
\n"); $PageEndFmt = array('
',&$HTMLEndFmt); $HandleActions = array( 'browse' => 'HandleBrowse', 'print' => 'HandleBrowse', 'edit' => 'HandleEdit', 'source' => 'HandleSource', 'attr' => 'HandleAttr', 'postattr' => 'HandlePostAttr', 'logout' => 'HandleLogoutA', 'login' => 'HandleLoginA'); $HandleAuth = array( 'browse' => 'read', 'source' => 'read', 'print' => 'read', 'edit' => 'edit', 'attr' => 'attr', 'postattr' => 'attr', 'logout' => 'read', 'login' => 'login'); $ActionTitleFmt = array( 'edit' => '| $[Edit]', 'attr' => '| $[Attributes]', 'login' => '| $[Login]'); $DefaultPasswords = array('admin'=>'*','read'=>'','edit'=>'','attr'=>''); $AuthCascade = array('edit'=>'read', 'attr'=>'edit'); $AuthList = array('' => 1, 'nopass:' => 1, '@nopass' => 1); $SessionEncode = 'base64_encode'; $SessionDecode = 'base64_decode'; $CallbackFnTemplates = array( 'default' => '%s', 'return' => 'return %s;', 'markup_e' => 'extract($GLOBALS["MarkupToHTML"]); return %s;', 'qualify' => 'extract($GLOBALS["tmp_qualify"]); return %s;', ); $Conditions['enabled'] = '(boolean)@$GLOBALS[$condparm]'; $Conditions['false'] = 'false'; $Conditions['true'] = 'true'; $Conditions['group'] = "(boolean)MatchPageNames(\$pagename, FixGlob(\$condparm, '$1$2.*'))"; $Conditions['name'] = "(boolean)MatchPageNames(\$pagename, FixGlob(\$condparm, '$1*.$2'))"; $Conditions['match'] = 'preg_match("!$condparm!",$pagename)'; $Conditions['authid'] = 'NoCache(@$GLOBALS["AuthId"] > "")'; $Conditions['exists'] = "(boolean)ListPages(FixGlob( str_replace(array('[[',']]'), array('', ''), \$condparm) , '$1*.$2'))"; $Conditions['equal'] = 'CompareArgs($condparm) == 0'; function CompareArgs($arg) { $arg = ParseArgs($arg); return strcmp(@$arg[''][0], @$arg[''][1]); } $Conditions['auth'] = 'NoCache(CondAuth($pagename, $condparm))'; function CondAuth($pagename, $condparm) { global $HandleAuth; @list($level, $pn) = explode(' ', $condparm, 2); $pn = ($pn > '') ? MakePageName($pagename, $pn) : $pagename; if (@$HandleAuth[$level]>'') $level = $HandleAuth[$level]; return (boolean)RetrieveAuthPage($pn, $level, false, READPAGE_CURRENT); } ## CondExpr handles complex conditions (expressions) ## Portions Copyright 2005 by D. Faure (dfaure@cpan.org) function CondExpr($pagename, $condname, $condparm) { global $CondExprOps; SDV($CondExprOps, 'and|x?or|&&|\\|\\||[!()]'); if ($condname == '(' || $condname == '[') $condparm = preg_replace('/[\\]\\)]\\s*$/', '', $condparm); $condparm = str_replace('&&', '&&', $condparm); $terms = preg_split("/(? $t) { $t = trim($t); if (preg_match("/^($CondExprOps)$/i", $t)) continue; if ($t) $terms[$i] = CondText($pagename, "if $t", 'TRUE') ? '1' : '0'; } return @eval('return(' . implode(' ', $terms) . ');'); } $Conditions['expr'] = 'CondExpr($pagename, $condname, $condparm)'; $Conditions['('] = 'CondExpr($pagename, $condname, $condparm)'; $Conditions['['] = 'CondExpr($pagename, $condname, $condparm)'; $MarkupTable['_begin']['seq'] = 'B'; $MarkupTable['_end']['seq'] = 'E'; Markup('fulltext','>_begin'); Markup('split','>fulltext',"\n", '$RedoMarkupLine=1; return explode("\n",$x);'); Markup('directives','>split'); Markup('inline','>directives'); Markup('links','>inline'); Markup('block','>links'); Markup('style','>block'); Markup_e('closeall', '_begin', '/^\\(:closeall:\\)$/', "'<:block>' . MarkupClose()"); $ImgExtPattern="\\.(?:gif|jpg|jpeg|png|GIF|JPG|JPEG|PNG)"; $ImgTagFmt="\$LinkAlt"; $BlockMarkups = array( 'block' => array('','','',0), 'ul' => array('',1), 'dl' => array('
','','
',1), 'ol' => array('
  1. ','
  2. ','
',1), 'p' => array('

','','

',0), 'indent' => array("
","
",'
',1), 'outdent' => array("
","
",'
',1), 'pre' => array('
','','
',0), 'table' => array("",'','
',0)); foreach(array('http:','https:','mailto:','ftp:','news:','gopher:','nap:', 'file:', 'tel:', 'geo:') as $m) { $LinkFunctions[$m] = 'LinkIMap'; $IMap[$m]="$m$1"; } $LinkFunctions['<:page>'] = 'LinkPage'; $q = preg_replace('/(\\?|%3f)([-\\w]+=)/i', '&$2', @$_SERVER['QUERY_STRING']); if ($q != @$_SERVER['QUERY_STRING']) { unset($_GET); parse_str($q, $_GET); $_REQUEST = array_merge($_REQUEST, $_GET, $_POST); } if (isset($_GET['action'])) $action = $_GET['action']; elseif (isset($_POST['action'])) $action = $_POST['action']; else $action = 'browse'; $pagename = @$_REQUEST['n']; if (!$pagename) $pagename = @$_REQUEST['pagename']; if (!$pagename && preg_match('!^'.preg_quote($_SERVER['SCRIPT_NAME'],'!').'/?([^?]*)!', $_SERVER['REQUEST_URI'],$match)) $pagename = urldecode($match[1]); if (preg_match('/[\\x80-\\xbf]/',$pagename)) $pagename=utf8_decode($pagename); $pagename = preg_replace('![^[:alnum:]\\x80-\\xff]+$!','',$pagename); $FmtPV['$RequestedPage'] = "'".PHSC($pagename, ENT_QUOTES)."'"; $Cursor['*'] = &$pagename; if (function_exists("date_default_timezone_get") ) { # fix PHP5.3 warnings @date_default_timezone_set(@date_default_timezone_get()); } if (file_exists("$FarmD/local/farmconfig.php")) include_once("$FarmD/local/farmconfig.php"); if (IsEnabled($EnableLocalConfig,1)) { if (file_exists("$LocalDir/config.php")) include_once("$LocalDir/config.php"); elseif (file_exists('config.php')) include_once('config.php'); } SDV($CurrentTime, strftime($TimeFmt, $Now)); SDV($CurrentTimeISO, strftime($TimeISOFmt, $Now)); if (IsEnabled($EnableStdConfig,1)) include_once("$FarmD/scripts/stdconfig.php"); if (isset($PostConfig) && is_array($PostConfig)) { asort($PostConfig, SORT_NUMERIC); foreach ($PostConfig as $k=>$v) { if (!$k || !$v || $v<50) continue; if (function_exists($k)) $k($pagename); elseif (file_exists($k)) include_once($k); } } foreach((array)$InterMapFiles as $f) { $f = FmtPageName($f, $pagename); if (($v = @file($f))) $v = preg_replace('/^\\s*(?>\\w[-\\w]*)(?!:)/m', '$0:', implode('', $v)); else if (PageExists($f)) { $p = ReadPage($f, READPAGE_CURRENT); $v = $p['text']; } else continue; if (!preg_match_all("/^\\s*(\\w[-\\w]*:)[^\\S\n]+(\\S*)/m", $v, $match, PREG_SET_ORDER)) continue; foreach($match as $m) { if (strpos($m[2], '$1') === false) $m[2] .= '$1'; $LinkFunctions[$m[1]] = 'LinkIMap'; $IMap[$m[1]] = FmtPageName($m[2], $pagename); } } $keys = array_keys($AuthCascade); while ($keys) { $k = array_shift($keys); $t = $AuthCascade[$k]; if (in_array($t, $keys)) { unset($AuthCascade[$k]); $AuthCascade[$k] = $t; array_push($keys, $k); } } $LinkPattern = implode('|',array_keys($LinkFunctions)); # after InterMaps SDV($LinkPageCreateSpaceFmt,$LinkPageCreateFmt); $ActionTitle = FmtPageName(@$ActionTitleFmt[$action], $pagename); if (!@$HandleActions[$action] || !function_exists($HandleActions[$action])) $action='browse'; if (IsEnabled($EnableActions, 1)) HandleDispatch($pagename, $action); Lock(0); return; ## HandleDispatch() is used to dispatch control to the appropriate ## action handler with the appropriate permissions. ## If a message is supplied, it is added to $MessagesFmt. function HandleDispatch($pagename, $action, $msg=NULL) { global $MessagesFmt, $HandleActions, $HandleAuth; if ($msg) $MessagesFmt[] = "
$msg
"; $fn = $HandleActions[$action]; $auth = $HandleAuth[$action]; if (!$auth) $auth = 'read'; return $fn($pagename, $auth); } ## helper functions function stripmagic($x) { return get_magic_quotes_gpc() ? stripslashes($x) : $x; } function pre_r(&$x) { return '
'.PHSC(print_r($x, true)).'
'; } function PSS($x) { return str_replace('\\"','"',$x); } function PVS($x) { return preg_replace("/\n[^\\S\n]*(?=\n)/", "\n<:vspace>", $x); } function PVSE($x) { return PVS(PHSC($x, ENT_NOQUOTES)); } function PZZ($x,$y='') { return ''; } function PRR($x=NULL) { if ($x || is_null($x)) $GLOBALS['RedoMarkupLine']++; return $x; } function PUE($x) { return PPRE('/[\\x80-\\xff \'"<>]/', "'%'.dechex(ord(\$m[0]))", $x); } function PQA($x) { $out = ''; if (preg_match_all('/([a-zA-Z][-\\w]*)\\s*=\\s*("[^"]*"|\'[^\']*\'|\\S*)/', $x, $attr, PREG_SET_ORDER)) { foreach($attr as $a) { if (preg_match('/^on/i', $a[1])) continue; $out .= $a[1] . '=' . PPRE( '/^([\'"]?)(.*)\\1$/', "\"'\".str_replace(\"'\", ''', \$m[2]).\"'\"", $a[2]) . ' '; } } return $out; } function SDV(&$v,$x) { if (!isset($v)) $v=$x; } function SDVA(&$var,$val) { foreach($val as $k=>$v) if (!isset($var[$k])) $var[$k]=$v; } function IsEnabled(&$var,$f=0) { return (isset($var)) ? $var : $f; } function SetTmplDisplay($var, $val) { NoCache(); $GLOBALS['TmplDisplay'][$var] = $val; } function NoCache($x = '') { $GLOBALS['NoHTMLCache'] |= 1; return $x; } function ParseArgs($x, $optpat = '(?>(\\w+)[:=])') { $z = array(); preg_match_all("/($optpat|[-+])?(\"[^\"]*\"|'[^']*'|\\S+)/", $x, $terms, PREG_SET_ORDER); foreach($terms as $t) { $v = preg_replace('/^([\'"])?(.*)\\1$/', '$2', $t[3]); if ($t[2]) { $z['#'][] = $t[2]; $z[$t[2]] = $v; } else { $z['#'][] = $t[1]; $z[$t[1]][] = $v; } $z['#'][] = $v; } return $z; } function PHSC($x, $flags=ENT_COMPAT, $enc=null) { # for PHP 5.4 if(is_null($enc)) $enc = "ISO-8859-1"; # $GLOBALS['Charset'] return htmlspecialchars($x, $flags, $enc); } function PCCF($code, $template = 'default', $args = '$m') { global $CallbackFnTemplates, $CallbackFunctions; if(!isset($CallbackFnTemplates[$template])) Abort("No \$CallbackFnTemplates[$template])."); $code = sprintf($CallbackFnTemplates[$template], $code); if(!isset($CallbackFunctions[$code])) { $fn = create_function($args, $code); if($fn) $CallbackFunctions[$code] = $fn; else StopWatch("Failed to create callback function: $code"); } return $CallbackFunctions[$code]; } function PPRE($pat, $rep, $x) { $lambda = PCCF("return $rep;"); return preg_replace_callback($pat, $lambda, $x); } function PPRA($array, $x) { foreach($array as $pat => $rep) { $fmt = $x; # for $FmtP if(is_callable($rep) && $rep != '_') $x = preg_replace_callback($pat,$rep,$x); else $x = preg_replace($pat,$rep,$x); } return $x; } function StopWatch($x) { global $StopWatch, $EnableStopWatch; if (!$EnableStopWatch) return; static $wstart = 0, $ustart = 0; list($usec,$sec) = explode(' ',microtime()); $wtime = ($sec+$usec); if (!$wstart) $wstart = $wtime; if ($EnableStopWatch != 2) { $StopWatch[] = sprintf("%05.2f %s", $wtime-$wstart, $x); return; } $dat = getrusage(); $utime = ($dat['ru_utime.tv_sec']+$dat['ru_utime.tv_usec']/1000000); if (!$ustart) $ustart=$utime; $StopWatch[] = sprintf("%05.2f %05.2f %s", $wtime-$wstart, $utime-$ustart, $x); } ## DRange converts a variety of string formats into date (ranges). ## It returns the start and end timestamps (+1 second) of the specified date. function DRange($when) { global $Now; ## unix/posix @timestamp dates if (preg_match('/^\\s*@(\\d+)\\s*(.*)$/', $when, $m)) { $t0 = $m[2] ? strtotime($m[2], $m[1]) : $m[1]; return array($t0, $t0+1); } ## ISO-8601 dates $dpat = '/ (?'' && @$m[5] == '') { @$n[4]++; } ## if no day given, assume 1st of month and full month range if (@$m[4] == '') { $m[4] = 1; $n[4] = 1; $n[3]++; } ## if no seconds given, assume range of 1 minute (except when full day) if (@$m[7]>'' && @$m[8] == '') { @$n[7]++; } $t0 = @mktime($m[5], $m[7], $m[8], $m[3], $m[4], $m[1]); $t1 = @mktime($n[5], $n[7], $n[8], $n[3], $n[4], $n[1]); return array($t0, $t1); } ## now, today, tomorrow, yesterday NoCache(); if ($when == 'now') return array($Now, $Now+1); $m = localtime(time()); if ($when == 'tomorrow') { $m[3]++; $when = 'today'; } if ($when == 'yesterday') { $m[3]--; $when = 'today'; } if ($when == 'today') return array(mktime(0, 0, 0, $m[4]+1, $m[3] , $m[5]+1900), mktime(0, 0, 0, $m[4]+1, $m[3]+1, $m[5]+1900)); if (preg_match('/^\\s*$/', $when)) return array(-1, -1); $t0 = strtotime($when); $t1 = strtotime("+1 day", $t0); return array($t0, $t1); } ## AsSpaced converts a string with WikiWords into a spaced version ## of that string. (It can be overridden via $AsSpacedFunction.) function AsSpaced($text) { $text = preg_replace("/([[:lower:]\\d])([[:upper:]])/", '$1 $2', $text); $text = preg_replace('/([^-\\d])(\\d[-\\d]*( |$))/','$1 $2',$text); return preg_replace("/([[:upper:]])([[:upper:]][[:lower:]\\d])/", '$1 $2', $text); } ## Lock is used to make sure only one instance of PmWiki is running when ## files are being written. It does not "lock pages" for editing. function Lock($op) { global $WorkDir, $LockFile, $EnableReadOnly; if ($op > 0 && IsEnabled($EnableReadOnly, 0)) Abort('Cannot modify site -- $EnableReadOnly is set', 'readonly'); SDV($LockFile, "$WorkDir/.flock"); mkdirp(dirname($LockFile)); static $lockfp,$curop; if (!$lockfp) $lockfp = @fopen($LockFile, "w"); if (!$lockfp) { if ($op <= 0) return; @unlink($LockFile); $lockfp = fopen($LockFile,"w") or Abort('Cannot acquire lockfile', 'flock'); fixperms($LockFile); } if ($op<0) { flock($lockfp,LOCK_UN); fclose($lockfp); $lockfp=0; $curop=0; } elseif ($op==0) { flock($lockfp,LOCK_UN); $curop=0; } elseif ($op==1 && $curop<1) { session_write_close(); flock($lockfp,LOCK_SH); $curop=1; } elseif ($op==2 && $curop<2) { session_write_close(); flock($lockfp,LOCK_EX); $curop=2; } } ## mkdirp creates a directory and its parents as needed, and sets ## permissions accordingly. function mkdirp($dir) { global $ScriptUrl; if (file_exists($dir)) return; if (!file_exists(dirname($dir))) mkdirp(dirname($dir)); if (mkdir($dir, 0777)) { fixperms($dir); if (@touch("$dir/xxx")) { unlink("$dir/xxx"); return; } rmdir($dir); } $parent = realpath(dirname($dir)); $bdir = basename($dir); $perms = decoct(fileperms($parent) & 03777); $msg = "PmWiki needs to have a writable $dir/ directory before it can continue. You can create the directory manually by executing the following commands on your server:
    mkdir $parent/$bdir\n    chmod 777 $parent/$bdir
Then, reload this page."; $safemode = ini_get('safe_mode'); if (!$safemode) $msg .= "

Or, for a slightly more secure installation, try executing
    chmod 2777 $parent
on 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, $set = 0) { clearstatcache(); if (!file_exists($fname)) Abort('?no such file'); if ($set) { # advanced admins, $UploadPermSet if (fileperms($fname) != $set) @chmod($fname,$set); } else { $bp = 0; if (fileowner($fname)!=@fileowner('.') && @fileowner('.')!==0) $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); } } ## GlobToPCRE converts wildcard patterns into pcre patterns for ## inclusion and exclusion. Wildcards beginning with '-' or '!' ## are treated as things to be excluded. function GlobToPCRE($pat) { $pat = preg_quote($pat, '/'); $pat = str_replace(array('\\*', '\\?', '\\[', '\\]', '\\^', '\\-', '\\!'), array('.*', '.', '[', ']', '^', '-', '!'), $pat); $excl = array(); $incl = array(); foreach(preg_split('/,+\s?/', $pat, -1, PREG_SPLIT_NO_EMPTY) as $p) { if ($p{0} == '-' || $p{0} == '!') $excl[] = '^'.substr($p, 1).'$'; else $incl[] = "^$p$"; } return array(implode('|', $incl), implode('|', $excl)); } ## FixGlob changes wildcard patterns without '.' to things like ## '*.foo' (name matches) or 'foo.*' (group matches). function FixGlob($x, $rep = '$1*.$2') { return preg_replace('/([\\s,][-!]?)([^\\/.\\s,]+)(?=[\\s,])/', $rep, ",$x,"); } ## MatchPageNames reduces $pagelist to those pages with names ## matching the pattern(s) in $pat. Patterns can be either ## regexes to include ('/'), regexes to exclude ('!'), or ## wildcard patterns (all others). function MatchPageNames($pagelist, $pat) { global $Charset, $EnableRangeMatchUTF8; # allow range matches in utf8; doesn't work on pmwiki.org and possibly elsewhere $pcre8 = (IsEnabled($EnableRangeMatchUTF8,0) && $Charset=='UTF-8')? 'u' : ''; $pagelist = (array)$pagelist; foreach((array)$pat as $p) { if (count($pagelist) < 1) break; if (!$p) continue; switch ($p{0}) { case '/': $pagelist = preg_grep($p, $pagelist); continue; case '!': $pagelist = array_diff($pagelist, preg_grep($p, $pagelist)); continue; default: list($inclp, $exclp) = GlobToPCRE(str_replace('/', '.', $p)); if ($exclp) $pagelist = array_diff($pagelist, preg_grep("/$exclp/i$pcre8", $pagelist)); if ($inclp) $pagelist = preg_grep("/$inclp/i$pcre8", $pagelist); } } return $pagelist; } ## ResolvePageName "normalizes" a pagename based on the current ## settings of $DefaultPage and $PagePathFmt. It's normally used ## during initialization to fix up any missing or partial pagenames. function ResolvePageName($pagename) { global $DefaultPage, $DefaultGroup, $DefaultName, $GroupPattern, $NamePattern, $EnableFixedUrlRedirect; SDV($DefaultPage, "$DefaultGroup.$DefaultName"); $pagename = preg_replace('!([./][^./]+)\\.html$!', '$1', $pagename); if ($pagename == '') return $DefaultPage; $p = MakePageName($DefaultPage, $pagename); if (!preg_match("/^($GroupPattern)[.\\/]($NamePattern)$/i", $p)) { header('HTTP/1.1 404 Not Found'); Abort('$[?invalid page name]'); } if (preg_match("/^($GroupPattern)[.\\/]($NamePattern)$/i", $pagename)) return $p; if (IsEnabled($EnableFixedUrlRedirect, 1) && $p && (PageExists($p) || preg_match('/[\\/.]/', $pagename))) { Redirect($p); exit(); } return MakePageName($DefaultPage, "$pagename.$pagename"); } ## MakePageName is used to convert a string $str into a fully-qualified ## pagename. If $str doesn't contain a group qualifier, then ## MakePageName uses $basepage and $PagePathFmt to determine the ## group of the returned pagename. function MakePageName($basepage, $str) { global $MakePageNameFunction, $PageNameChars, $PagePathFmt, $MakePageNamePatterns, $MakePageNameSplitPattern; if (@$MakePageNameFunction) return $MakePageNameFunction($basepage, $str); SDV($PageNameChars,'-[:alnum:]'); SDV($MakePageNamePatterns, array( "/'/" => '', # strip single-quotes "/[^$PageNameChars]+/" => ' ', # convert everything else to space '/((^|[^-\\w])\\w)/' => PCCF("return strtoupper(\$m[1]);"), '/ /' => '')); SDV($MakePageNameSplitPattern, '/[.\\/]/'); $str = preg_replace('/[#?].*$/', '', $str); $m = preg_split($MakePageNameSplitPattern, $str); if (count($m)<1 || count($m)>2 || $m[0]=='') return ''; ## handle "Group.Name" conversions if (@$m[1] > '') { $group = PPRA($MakePageNamePatterns, $m[0]); $name = PPRA($MakePageNamePatterns, $m[1]); return "$group.$name"; } $name = PPRA($MakePageNamePatterns, $m[0]); $isgrouphome = count($m) > 1; foreach((array)$PagePathFmt as $pg) { if ($isgrouphome && strncmp($pg, '$1.', 3) !== 0) continue; $pn = FmtPageName(str_replace('$1', $name, $pg), $basepage); if (PageExists($pn)) return $pn; } if ($isgrouphome) { foreach((array)$PagePathFmt as $pg) if (strncmp($pg, '$1.', 3) == 0) return FmtPageName(str_replace('$1', $name, $pg), $basepage); return "$name.$name"; } return preg_replace('/[^\\/.]+$/', $name, $basepage); } ## MakeBaseName uses $BaseNamePatterns to return the "base" form ## of a given pagename -- i.e., stripping any recipe-defined ## prefixes or suffixes from the page. function MakeBaseName($pagename, $patlist = NULL) { global $BaseNamePatterns; if (is_null($patlist)) $patlist = (array)@$BaseNamePatterns; foreach($patlist as $pat => $rep) $pagename = preg_replace($pat, $rep, $pagename); # TODO return $pagename; } ## 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 PageVar below. function PCache($pagename, $page) { global $PCache; foreach($page as $k=>$v) if ($k!='text' && strpos($k,':')===false) $PCache[$pagename][$k]=$v; } ## SetProperty saves a page property into $PCache. For convenience ## it returns the $value of the property just set. If $sep is supplied, ## then $value is appended to the current property (with $sep as ## as separator) instead of replacing it. If $keep is suplied and the ## property already exists, then $value will be ignored. function SetProperty($pagename, $prop, $value, $sep=NULL, $keep=NULL) { global $PCache, $KeepToken; NoCache(); $prop = "=p_$prop"; $value = PPRE("/$KeepToken(\\d.*?)$KeepToken/", "\$GLOBALS['KPV'][\$m[1]]", $value); if (!is_null($sep) && isset($PCache[$pagename][$prop])) $value = $PCache[$pagename][$prop] . $sep . $value; if (is_null($keep) || !isset($PCache[$pagename][$prop])) $PCache[$pagename][$prop] = $value; return $PCache[$pagename][$prop]; } ## PageTextVar loads a page's text variables (defined by ## $PageTextVarPatterns) into a page's $PCache entry, and returns ## the property associated with $var. function PageTextVar($pagename, $var) { global $PCache, $PageTextVarPatterns, $MaxPageTextVars; SDV($MaxPageTextVars, 500); static $status; if(@$status["$pagename:$var"]++ > $MaxPageTextVars) return ''; if (!@$PCache[$pagename]['=pagetextvars']) { $pc = &$PCache[$pagename]; $pc['=pagetextvars'] = 1; $page = RetrieveAuthPage($pagename, 'read', false, READPAGE_CURRENT); if ($page) { foreach((array)$PageTextVarPatterns as $pat) if (preg_match_all($pat, IsEnabled($PCache[$pagename]['=preview'],@$page['text']), $match, PREG_SET_ORDER)) foreach($match as $m) { $t = preg_replace("/\\{\\$:{$m[2]}\\}/", '', $m[3]); $pc["=p_{$m[2]}"] = Qualify($pagename, $t); } } } return @$PCache[$pagename]["=p_$var"]; } function PageVar($pagename, $var, $pn = '') { global $Cursor, $PCache, $FmtPV, $AsSpacedFunction, $ScriptUrl, $EnablePathInfo; if ($var == '$ScriptUrl') return PUE($ScriptUrl); if ($pn) { $pn = isset($Cursor[$pn]) ? $Cursor[$pn] : MakePageName($pagename, $pn); } else $pn = $pagename; if ($pn) { if (preg_match('/^(.+)[.\\/]([^.\\/]+)$/', $pn, $match) && !isset($PCache[$pn]['time']) && (!@$FmtPV[$var] || strpos($FmtPV[$var], '$page') !== false)) { $page = ReadPage($pn, READPAGE_CURRENT); PCache($pn, $page); } @list($d, $group, $name) = $match; $page = &$PCache[$pn]; if(strpos($FmtPV[$var], '$authpage') !== false) { if(!isset($page['=auth']['read'])) { $x = RetrieveAuthPage($pn, 'read', false, READPAGE_CURRENT); if($x) PCache($pn, $x); } if(@$page['=auth']['read']) $authpage = &$page; } } else { $group = ''; $name = ''; } if (@$FmtPV[$var]) return eval("return ({$FmtPV[$var]});"); if (strncmp($var, '$:', 2)==0) return PageTextVar($pn, substr($var, 2)); return ''; } ## 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, $FmtPV, $PCache, $AsSpacedFunction; if (strpos($fmt,'$')===false) return $fmt; $fmt = PPRE('/\\$([A-Z]\\w*Fmt)\\b/','$GLOBALS[$m[1]]',$fmt); $fmt = PPRE('/\\$\\[(?>([^\\]]+))\\]/',"XL(\$m[1])",$fmt); $fmt = str_replace('{$ScriptUrl}', '$ScriptUrl', $fmt); $fmt = PPRE('/\\{(\\$[A-Z]\\w+)\\}/', "PageVar('$pagename', \$m[1])", $fmt); if (strpos($fmt,'$')===false) return $fmt; if ($FmtP) $fmt = PPRA($FmtP, $fmt); # FIXME static $pv, $pvpat; if ($pv != count($FmtPV)) { $pvpat = str_replace('$', '\\$', implode('|', array_keys($FmtPV))); $pv = count($FmtPV); } $fmt = PPRE("/(?:$pvpat)\\b/", "PageVar('$pagename', \$m[0])", $fmt); $fmt = PPRE('!\\$ScriptUrl/([^?#\'"\\s<>]+)!', (@$EnablePathInfo) ? "'$ScriptUrl/'.PUE(\$m[1])" : "'$ScriptUrl?n='.str_replace('/','.',PUE(\$m[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 = PPRE('/(?>(\\$[[:alpha:]]\\w+))/', "isset(\$GLOBALS['FmtV'][\$m[1]]) ? \$GLOBALS['FmtV'][\$m[1]] : \$m[1]", $fmt); return $fmt; } ## FmtPageTitle returns the page title, or the page name ## It localizes standard technical pages (RecentChanges...) function FmtPageTitle($title, $name, $spaced=0) { if($title>'') return str_replace("$", "$", $title); global $SpaceWikiWords, $AsSpacedFunction; if(preg_match("/^(Site(Admin)? |(All)?(Site|Group)(Header|Footer|Attributes) |(Side|Left|Right)Bar |(Wiki)?Sand[Bb]ox |(All)?Recent(Changes|Uploads)|(Auth|Edit)Form |InterMap|PageActions|\\w+QuickReference|\\w+Templates |NotifyList|AuthUser|ApprovedUrls|(Block|Auth)List )$/x", $name) && $name != XL($name)) return XL($name); return ($spaced || $SpaceWikiWords) ? $AsSpacedFunction($name) : $name; } ## FmtTemplateVars uses $vars to replace all occurrences of ## {$$key} in $text with $vars['key']. function FmtTemplateVars($text, $vars, $pagename = NULL) { global $FmtPV, $EnableUndefinedTemplateVars; if ($pagename) { $pat = implode('|', array_map('preg_quote', array_keys($FmtPV))); $text = PPRE("/\\{\\$($pat)\\}/", "PageVar('$pagename', \$m[1])", $text); } foreach(preg_grep('/^[\\w$]/', array_keys($vars)) as $k) if (!is_array($vars[$k])) $text = str_replace("{\$\$$k}", $vars[$k], $text); if(! IsEnabled($EnableUndefinedTemplateVars, 0)) $text = preg_replace("/\\{\\$\\$\\w+\\}/", '', $text); return $text; } ## 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])) { if(preg_match('/^e_(rows|cols)$/', $k)) $v = intval($v); elseif(preg_match('/^ak_/', $k)) $v = $v{0}; $XL[$lang][$k]=$v; } } } function XLPage($lang,$p,$nohtml=false) { global $TimeFmt,$XLLangs,$FarmD, $EnableXLPageScriptLoad; $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,$m)) $xl[stripslashes($m[1])] = stripslashes($nohtml? PHSC($m[2]): $m[2]); if (isset($xl)) { if (IsEnabled($EnableXLPageScriptLoad, 0) && @$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']; if (!in_array($lang, $XLLangs)) 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; var $iswrite; var $encodefilenames; var $attr; var $recodefn; function PageStore($d='$WorkDir/$FullName', $w=0, $a=NULL) { $this->dirfmt = $d; $this->iswrite = $w; $this->attr = (array)$a; $GLOBALS['PageExistsCache'] = array(); # can we rely on iconv() or on mb_convert_encoding() ? if (function_exists('iconv') && @iconv("UTF-8", "WINDOWS-1252//IGNORE", "te\xd0\xafst")=='test' ) $this->recodefn = create_function('$s,$from,$to', 'return @iconv($from,"$to//IGNORE",$s);'); elseif (function_exists('mb_convert_encoding') && @mb_convert_encoding("te\xd0\xafst", "WINDOWS-1252", "UTF-8")=="te?st") $this->recodefn = create_function('$s,$from,$to', 'return @mb_convert_encoding($s,$to,$from);'); else $this->recodefn = false; } function pagefile($pagename) { global $FarmD; $dfmt = $this->dirfmt; if ($pagename > '') { $pagename = str_replace('/', '.', $pagename); if ($dfmt == 'wiki.d/{$FullName}') # optimizations for return $this->PFE("wiki.d/$pagename"); # standard locations if ($dfmt == '$FarmD/wikilib.d/{$FullName}') # return $this->PFE("$FarmD/wikilib.d/$pagename"); if ($dfmt == 'wiki.d/{$Group}/{$FullName}') return $this->PFE(preg_replace('/([^.]+).*/', 'wiki.d/$1/$0', $pagename)); } return $this->PFE(FmtPageName($dfmt, $pagename)); } function PFE($f) { # pagefile_encode if (!$this->encodefilenames) return $f; global $PageFileEncodeFunction; return $PageFileEncodeFunction($f); } function PFD($f) { # pagefile_decode if (!$this->encodefilenames) return $f; global $PageFileDecodeFunction; return $PageFileDecodeFunction($f); } function read($pagename, $since=0) { $newline = ''; $urlencoded = false; $pagefile = $this->pagefile($pagename); if ($pagefile && ($fp=@fopen($pagefile, "r"))) { $page = $this->attr; 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.')!==false) $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 $this->recode($pagename, @$page); } function write($pagename,$page) { global $Now, $Version, $Charset; $page['charset'] = $Charset; $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"; $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) { if (!$pagename) return false; $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; StopWatch("PageStore::ls begin {$this->dirfmt}"); $pats=(array)$pats; array_push($pats, "/^$GroupPattern\.$NamePattern$/"); $dir = $this->pagefile('$Group.$Name'); $maxslash = substr_count($dir, '/'); $dirlist = array(preg_replace('!/*[^/]*\\$.*$!','',$dir)); $out = array(); while (count($dirlist)>0) { $dir = array_shift($dirlist); $dfp = @opendir($dir); if (!$dfp) { continue; } $dirslash = substr_count($dir, '/') + 1; $o = array(); while ( ($pagefile = readdir($dfp)) !== false) { if ($pagefile{0} == '.') continue; if ($dirslash < $maxslash && is_dir("$dir/$pagefile")) { array_push($dirlist,"$dir/$pagefile"); continue; } if ($dirslash == $maxslash) $o[] = $this->PFD($pagefile); } closedir($dfp); StopWatch("PageStore::ls merge {$this->dirfmt}"); $out = array_merge($out, MatchPageNames($o, $pats)); } StopWatch("PageStore::ls end {$this->dirfmt}"); return $out; } function recode($pagename, $a) { if(!$a) return false; global $Charset, $PageRecodeFunction, $DefaultPageCharset, $EnableOldCharset; if (function_exists($PageRecodeFunction)) return $PageRecodeFunction($a); if (IsEnabled($EnableOldCharset)) $a['=oldcharset'] = @$a['charset']; SDVA($DefaultPageCharset, array(''=>@$Charset)); # pre-2.2.31 RecentChanges if (@$DefaultPageCharset[$a['charset']]>'') # wrong pre-2.2.30 encs. *-2, *-9, *-13 $a['charset'] = $DefaultPageCharset[@$a['charset']]; if (!$a['charset'] || $Charset==$a['charset']) return $a; $from = ($a['charset']=='ISO-8859-1') ? 'WINDOWS-1252' : $a['charset']; $to = ($Charset=='ISO-8859-1') ? 'WINDOWS-1252' : $Charset; if ($this->recodefn) $F = $this->recodefn; elseif ($to=='UTF-8' && $from=='WINDOWS-1252') # utf8 wiki & pre-2.2.30 doc $F = create_function('$s,$from,$to', 'return utf8_encode($s);'); elseif ($to=='WINDOWS-1252' && $from=='UTF-8') # 2.2.31+ documentation $F = create_function('$s,$from,$to', 'return utf8_decode($s);'); else return $a; foreach($a as $k=>$v) $a[$k] = $F($v,$from,$to); $a['charset'] = $Charset; return $a; } } 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) $page['ctime'] = $Now; if (@!$page['time']) $page['time'] = $Now; return $page; } function WritePage($pagename,$page) { global $WikiLibDirs,$WikiDir,$LastModFile; $WikiDir->iswrite = 1; for($i=0; $iiswrite && $wd->exists($pagename)) break; } if ($i >= count($WikiLibDirs)) $wd = &$WikiDir; $wd->write($pagename,$page); if ($LastModFile && !@touch($LastModFile)) { unlink($LastModFile); touch($LastModFile); fixperms($LastModFile); } } function PageExists($pagename) { ## note: $PageExistsCache might change or disappear someday global $WikiLibDirs, $PageExistsCache; if (!isset($PageExistsCache[$pagename])) { foreach((array)$WikiLibDirs as $dir) if ($PageExistsCache[$pagename] = $dir->exists($pagename)) break; } return $PageExistsCache[$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, $info='') { # exit pmwiki with an abort message global $ScriptUrl, $Charset, $AbortFunction; if(@$AbortFunction) return $AbortFunction($msg, $info); if ($info) $info = "

$[More information]

"; $msg = "

$[PmWiki can't process your request]

$msg

$[We are sorry for any inconvenience].

$info

$[Return to] $ScriptUrl

"; @header("Content-type: text/html; charset=$Charset"); echo PPRE('/\\$\\[([^\\]]+)\\]/', "XL(\$m[1])", $msg); exit; } function Redirect($pagename, $urlfmt='$PageUrl') { # redirect the browser to $pagename global $EnableRedirect, $RedirectDelay, $EnableStopWatch; SDV($RedirectDelay, 0); clearstatcache(); $pageurl = FmtPageName($urlfmt,$pagename); if (IsEnabled($EnableRedirect,1) && (!isset($_REQUEST['redirect']) || $_REQUEST['redirect'])) { header("Location: $pageurl"); header("Content-type: text/html"); echo " Redirect"; exit; } echo "Redirect to $pageurl"; if (@$EnableStopWatch && function_exists('StopWatchHTML')) StopWatchHTML($pagename, 1); exit; } function PrintFmt($pagename,$fmt) { global $HTTPHeaders,$FmtV; if (is_array($fmt)) { foreach($fmt as $f) PrintFmt($pagename,$f); return; } if ($fmt == 'headers:') { foreach($HTTPHeaders as $h) (@$sent++) ? @header($h) : header($h); return; } $x = FmtPageName($fmt,$pagename); if (strncmp($fmt, 'function:', 9) == 0 && preg_match('/^function:(\S+)\s*(.*)$/s', $x, $match) && function_exists($match[1])) { $match[1]($pagename,$match[2]); return; } if (strncmp($fmt, 'file:', 5) == 0 && preg_match("/^file:(.+)/s",$x,$match)) { $filelist = preg_split('/[\\s]+/',$match[1],-1,PREG_SPLIT_NO_EMPTY); foreach($filelist as $f) { if (file_exists($f)) { include($f); return; } } return; } if (substr($x, 0, 7) == 'markup:') { print MarkupToHTML($pagename, substr($x, 7)); return; } if (substr($x, 0, 5) == 'wiki:') { PrintWikiPage($pagename, substr($x, 5), 'read'); return; } if (substr($x, 0, 5) == 'page:') { PrintWikiPage($pagename, substr($x, 5), ''); return; } echo $x; } function PrintWikiPage($pagename, $wikilist=NULL, $auth='read') { if (is_null($wikilist)) $wikilist=$pagename; $pagelist = preg_split('/\s+/',$wikilist,-1,PREG_SPLIT_NO_EMPTY); foreach($pagelist as $p) { if (PageExists($p)) { $page = ($auth) ? RetrieveAuthPage($p, $auth, false, READPAGE_CURRENT) : ReadPage($p, READPAGE_CURRENT); if ($page['text']) echo MarkupToHTML($pagename,Qualify($p, $page['text'])); return; } } } function Keep($x, $pool=NULL) { # Keep preserves a string from being processed by wiki markups global $BlockPattern, $KeepToken, $KPV, $KPCount; $x = PPRE("/$KeepToken(\\d.*?)$KeepToken/", "\$GLOBALS['KPV'][\$m[1]]", $x); if (is_null($pool) && preg_match("! 0 && $text[$pos-1] != "\n") $pos--; else $pos += strlen("[[#$aa]]"); $text = substr($text, $pos); } if ($bb) $text = preg_replace("/(\n)[^\n]*\\[\\[#$bb\\]\\].*$/s", '$1', $text, 1); return $text; } ## RetrieveAuthSection extracts a section of text from a page. ## If $pagesection starts with anything other than '#', it identifies ## the page to extract text from. Otherwise RetrieveAuthSection looks ## in the pages given by $list, or in $pagename if $list is not specified. ## The selected page is placed in the global $RASPageName variable. ## The caller is responsible for calling Qualify() as needed. function RetrieveAuthSection($pagename, $pagesection, $list=NULL, $auth='read') { global $RASPageName, $PCache; if ($pagesection{0} != '#') $list = array(MakePageName($pagename, $pagesection)); else if (is_null($list)) $list = array($pagename); foreach((array)$list as $t) { $t = FmtPageName($t, $pagename); if (!PageExists($t)) continue; $tpage = RetrieveAuthPage($t, $auth, false, READPAGE_CURRENT); if (!$tpage) continue; $text = TextSection(IsEnabled($PCache[$t]['=preview'],$tpage['text']),$pagesection); if ($text !== false) { $RASPageName = $t; return $text; } } $RASPageName = ''; return false; } function IncludeText($pagename, $inclspec) { global $MaxIncludes, $IncludeOpt, $InclCount, $PCache; SDV($MaxIncludes,50); SDVA($IncludeOpt, array('self'=>1)); if ($InclCount++>=$MaxIncludes) return Keep($inclspec); $args = array_merge($IncludeOpt, ParseArgs($inclspec)); while (count($args['#'])>0) { $k = array_shift($args['#']); $v = array_shift($args['#']); if ($k=='') { if ($v{0} != '#') { if (isset($itext)) continue; $iname = MakePageName($pagename, $v); if (!$args['self'] && $iname == $pagename) continue; $ipage = RetrieveAuthPage($iname, 'read', false, READPAGE_CURRENT); $itext = IsEnabled($PCache[$iname]['=preview'], @$ipage['text']); } $itext = TextSection($itext, $v, array('anchors' => 1)); continue; } if (preg_match('/^(?:line|para)s?$/', $k)) { preg_match('/^(\\d*)(\\.\\.(\\d*))?$/', $v, $match); @list($x, $a, $dots, $b) = $match; $upat = ($k{0} == 'p') ? ".*?(\n\\s*\n|$)" : "[^\n]*(?:\n|$)"; if (!$dots) { $b=$a; $a=0; } if ($a>0) $a--; $itext=preg_replace("/^(($upat){0,$b}).*$/s",'$1',$itext,1); $itext=preg_replace("/^($upat){0,$a}/s",'',$itext,1); continue; } } $basepage = isset($args['basepage']) ? MakePageName($pagename, $args['basepage']) : $iname; if ($basepage) $itext = Qualify(@$basepage, @$itext); return FmtTemplateVars(PVSE($itext), $args); } function RedirectMarkup($pagename, $opt) { $k = Keep("(:redirect $opt:)"); global $MarkupFrame, $EnableRedirectQuiet; if (!@$MarkupFrame[0]['redirect']) return $k; $opt = ParseArgs($opt); $to = @$opt['to']; if (!$to) $to = @$opt[''][0]; if (!$to) return $k; if (preg_match('/^([^#]+)(#[A-Za-z][-\\w]*)$/', $to, $match)) { $to = $match[1]; $anchor = @$match[2]; } $to = MakePageName($pagename, $to); if (!PageExists($to)) return $k; if ($to == $pagename) return ''; if (@$opt['from'] && !MatchPageNames($pagename, FixGlob($opt['from'], '$1*.$2'))) return ''; if (preg_match('/^30[1237]$/', @$opt['status'])) header("HTTP/1.1 {$opt['status']}"); Redirect($to, "{\$PageUrl}" . (IsEnabled($EnableRedirectQuiet, 0) && IsEnabled($opt['quiet'], 0) ? '' : "?from=$pagename") . $anchor); exit(); } function Block($b) { global $BlockMarkups,$HTMLVSpace,$HTMLPNewline,$MarkupFrame; $mf = &$MarkupFrame[0]; $cs = &$mf['cs']; $vspaces = &$mf['vs']; $out = ''; if ($b == 'vspace') { $vspaces .= "\n"; while (count($cs)>0 && @end($cs)!='pre' && @$BlockMarkups[@end($cs)][3]==0) { $c = array_pop($cs); $out .= $BlockMarkups[$c][2]; } return $out; } @list($code, $depth, $icol) = explode(',', $b); if (!$code) $depth = 1; if ($depth == 0) $depth = strlen($depth); if ($icol == 0) $icol = strlen($icol); if ($depth > 0) $depth += @$mf['idep']; if ($icol > 0) $mf['is'][$depth] = $icol + @$mf['icol']; @$mf['idep'] = @$mf['icol'] = 0; while (count($cs)>$depth) { $c = array_pop($cs); $out .= $BlockMarkups[$c][2]; } if (!$code) { if (@end($cs) == 'p') { $out .= $HTMLPNewline; $code = 'p'; } else if ($depth < 2) { $code = 'p'; $mf['is'][$depth] = 0; } else { $out .= $HTMLPNewline; $code = 'block'; } } if ($depth>0 && $depth==count($cs) && $cs[$depth-1]!=$code) { $c = array_pop($cs); $out .= $BlockMarkups[$c][2]; } while (count($cs)>0 && @end($cs)!=$code && @$BlockMarkups[@end($cs)][3]==0) { $c = array_pop($cs); $out .= $BlockMarkups[$c][2]; } if ($vspaces) { $out .= (@end($cs) == 'pre') ? $vspaces : $HTMLVSpace; $vspaces=''; } if ($depth==0) { return $out; } if ($depth==count($cs)) { return $out.$BlockMarkups[$code][1]; } while (count($cs)<$depth-1) { array_push($cs, 'dl'); $mf['is'][count($cs)] = 0; $out .= $BlockMarkups['dl'][0].'
'; } if (count($cs)<$depth) { array_push($cs,$code); $out .= $BlockMarkups[$code][0]; } return $out; } function MarkupClose($key = '') { global $MarkupFrame; $cf = & $MarkupFrame[0]['closeall']; $out = ''; if ($key == '' || isset($cf[$key])) { $k = array_keys((array)$cf); while ($k) { $x = array_pop($k); $out .= $cf[$x]; unset($cf[$x]); if ($x == $key) break; } } return $out; } function FormatTableRow($x, $sep = '\\|\\|') { global $TableCellAttrFmt, $TableCellAlignFmt, $TableRowAttrFmt, $TableRowIndexMax, $MarkupFrame, $FmtV; static $rowcount; SDV($TableCellAlignFmt, " align='%s'"); $x = preg_replace("/$sep\\s*$/",'',$x); $td = preg_split("/$sep/", $x); $y = ''; for($i=0;$i1) { $attr .= " colspan='$colspan'"; } $y .= "<$t $attr>".trim($td[$i]).""; } if ($t=='caption') return "<:table,1>$y"; if (@$MarkupFrame[0]['cs'][0] != 'table') $rowcount = 0; else $rowcount++; $FmtV['$TableRowCount'] = $rowcount + 1; $FmtV['$TableRowIndex'] = ($rowcount % $TableRowIndexMax) + 1; $trattr = FmtPageName(@$TableRowAttrFmt, ''); return "<:table,1>$y"; } function LinkIMap($pagename,$imap,$path,$alt,$txt,$fmt=NULL) { global $FmtV, $IMap, $IMapLinkFmt, $UrlLinkFmt, $IMapLocalPath; SDVA($IMapLocalPath, array('Path:'=>1)); if(@$IMapLocalPath[$imap]) { $path = preg_replace('/^(\\w+):/', "$1%3a", $path); # PITS:01260 } $FmtV['$LinkUrl'] = PUE(str_replace('$1',$path,$IMap[$imap])); $FmtV['$LinkText'] = $txt; $FmtV['$LinkAlt'] = str_replace(array('"',"'"),array('"','''),$alt); if (!$fmt) $fmt = (isset($IMapLinkFmt[$imap])) ? $IMapLinkFmt[$imap] : $UrlLinkFmt; return str_replace(array_keys($FmtV),array_values($FmtV),$fmt); } function LinkPage($pagename,$imap,$path,$alt,$txt,$fmt=NULL) { global $QueryFragPattern, $LinkPageExistsFmt, $LinkPageSelfFmt, $LinkPageCreateSpaceFmt, $LinkPageCreateFmt, $LinkTargets, $EnableLinkPageRelative; $alt = str_replace(array('"',"'"),array('"','''),$alt); if (!$fmt && $path{0} == '#') { $path = preg_replace("/[^-.:\\w]/", '', $path); if (trim($txt) == '+') $txt = PageVar($pagename, '$Title'); if($alt) $alt = " title='$alt'"; return ($path) ? "".str_replace("$", "$", $txt)."" : ''; } if (!preg_match("/^\\s*([^#?]+)($QueryFragPattern)?$/",$path,$match)) return ''; $tgtname = MakePageName($pagename, $match[1]); if (!$tgtname) return ''; $qf = @$match[2]; @$LinkTargets[$tgtname]++; if (!$fmt) { if (!PageExists($tgtname) && !preg_match('/[&?]action=/', $qf)) $fmt = preg_match('/\\s/', $txt) ? $LinkPageCreateSpaceFmt : $LinkPageCreateFmt; else $fmt = ($tgtname == $pagename && $qf == '') ? $LinkPageSelfFmt : $LinkPageExistsFmt; } $url = PageVar($tgtname, '$PageUrl'); if (trim($txt) == '+') $txt = PageVar($tgtname, '$Title'); $txt = str_replace("$", "$", $txt); if (@$EnableLinkPageRelative) $url = preg_replace('!^[a-z]+://[^/]*!i', '', $url); $fmt = str_replace(array('$LinkUrl', '$LinkText', '$LinkAlt'), array($url.PUE($qf), $txt, $alt), $fmt); return FmtPageName($fmt,$tgtname); } function MakeLink($pagename,$tgt,$txt=NULL,$suffix=NULL,$fmt=NULL) { global $LinkPattern,$LinkFunctions,$UrlExcludeChars,$ImgExtPattern,$ImgTagFmt, $LinkTitleFunction; $t = preg_replace('/[()]/','',trim($tgt)); $t = preg_replace('/<[^>]*>/','',$t); preg_match("/^($LinkPattern)?(.+?)(\"(.*)\")?$/",$t,$m); if (!$m[1]) $m[1]='<:page>'; if (preg_match("/\"(.*)\"$/",trim($tgt),$x)) $m[4]=$x[1]; if (preg_match("/(($LinkPattern)([^$UrlExcludeChars]+$ImgExtPattern))(\"(.*)\")?$/",$txt,$tm)) $txt = $LinkFunctions[$tm[2]]($pagename,$tm[2],$tm[3],@$tm[5], $tm[1],$ImgTagFmt); else { if (is_null($txt)) { $txt = preg_replace('/\\([^)]*\\)/','',$tgt); if (@$m[3]) $txt = preg_replace('/"(.*)"(\\s*)$/','$2',$txt); if ($m[1]=='<:page>') { $txt = preg_replace('!/\\s*$!', '', $txt); $txt = preg_replace('!^.*[^<]/!', '', $txt); } } $txt .= $suffix; } if (@$LinkTitleFunction) $m[4] = $LinkTitleFunction($pagename,$m,$txt); $out = $LinkFunctions[$m[1]]($pagename,$m[1],$m[2],@$m[4],$txt,$fmt); return preg_replace('/(<[^>]+) title=(""|\'\')/', '$1', $out); } function Markup($id, $when, $pat=NULL, $rep=NULL) { global $MarkupTable; unset($GLOBALS['MarkupRules']); if (preg_match('/^([<>])?(.+)$/', $when, $m)) { $MarkupTable[$id]['cmd'] = $when; $MarkupTable[$m[2]]['dep'][$id] = $m[1]; if (!$m[1]) $m[1] = '='; if (@$MarkupTable[$m[2]]['seq']) { $MarkupTable[$id]['seq'] = $MarkupTable[$m[2]]['seq'].$m[1]; foreach((array)@$MarkupTable[$id]['dep'] as $i=>$m) Markup($i,"$m$id"); unset($GLOBALS['MarkupTable'][$id]['dep']); } } if ($pat && !isset($MarkupTable[$id]['pat'])) { $MarkupTable[$id]['pat'] = $pat; $MarkupTable[$id]['rep'] = $rep; if (preg_match('!/[^/]*e[^/]*$!', $pat)) { if (function_exists('debug_backtrace')) { $dbg = debug_backtrace(); $MarkupTable[$id]['dbg'] = "! file: {$dbg['0']['file']}, " . "line: {$dbg['0']['line']}, pat: {$dbg['0']['args'][2]}"; } else $MarkupTable[$id]['dbg'] = "! id: '$id', pat: '$pat'"; } } } function Markup_e($id, $when, $pat, $rep, $template = 'markup_e') { if (!is_callable($rep)) $rep = PCCF($rep, $template); Markup($id, $when, $pat, $rep); } function DisableMarkup() { global $MarkupTable; $idlist = func_get_args(); unset($GLOBALS['MarkupRules']); while (count($idlist)>0) { $id = array_shift($idlist); if (is_array($id)) { $idlist = array_merge($idlist, $id); continue; } $MarkupTable[$id] = array('cmd' => 'none', 'pat'=>''); } } function mpcmp($a,$b) { return @strcmp($a['seq'].'=',$b['seq'].'='); } function BuildMarkupRules() { global $MarkupTable,$MarkupRules,$LinkPattern; if (!$MarkupRules) { uasort($MarkupTable,'mpcmp'); foreach($MarkupTable as $id=>$m) if (@$m['pat'] && @$m['seq']) $MarkupRules[str_replace('\\L',$LinkPattern,$m['pat'])]=$m['rep']; } return $MarkupRules; } function MarkupToHTML($pagename, $text, $opt = NULL) { # convert wiki markup text to HTML output global $MarkupRules, $MarkupFrame, $MarkupFrameBase, $WikiWordCount, $K0, $K1, $RedoMarkupLine, $MarkupToHTML; $MarkupToHTML['pagename'] = $pagename; StopWatch('MarkupToHTML begin'); array_unshift($MarkupFrame, array_merge($MarkupFrameBase, (array)$opt)); $MarkupFrame[0]['wwcount'] = $WikiWordCount; foreach((array)$text as $l) $lines[] = $MarkupFrame[0]['escape'] ? PVSE($l) : $l; $lines[] = '(:closeall:)'; $out = ''; while (count($lines)>0) { $x = array_shift($lines); $RedoMarkupLine=0; $markrules = BuildMarkupRules(); foreach($markrules as $p=>$r) { if ($p{0} == '/') { if(is_callable($r)) $x = preg_replace_callback($p,$r,$x); else $x=preg_replace($p,$r,$x); } elseif (strstr($x,$p)!==false) $x=eval($r); if (isset($php_errormsg)) { echo "ERROR: pat=$p $php_errormsg"; unset($php_errormsg); } if ($RedoMarkupLine) { $lines=array_merge((array)$x,$lines); continue 2; } } if ($x>'') $out .= "$x\n"; } foreach((array)(@$MarkupFrame[0]['posteval']) as $v) eval($v); array_shift($MarkupFrame); StopWatch('MarkupToHTML end'); return $out; } function HandleBrowse($pagename, $auth = 'read') { # handle display of a page global $DefaultPageTextFmt, $PageNotFoundHeaderFmt, $HTTPHeaders, $EnableHTMLCache, $NoHTMLCache, $PageCacheFile, $LastModTime, $IsHTMLCached, $FmtV, $HandleBrowseFmt, $PageStartFmt, $PageEndFmt, $PageRedirectFmt; $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT); if (!$page) Abort("?cannot read $pagename"); PCache($pagename,$page); if (PageExists($pagename)) $text = @$page['text']; else { SDV($DefaultPageTextFmt,'(:include $[{$SiteGroup}.PageNotFound]:)'); $text = FmtPageName($DefaultPageTextFmt, $pagename); SDV($PageNotFoundHeaderFmt, 'HTTP/1.1 404 Not Found'); SDV($HTTPHeaders['status'], $PageNotFoundHeaderFmt); } $opt = array(); SDV($PageRedirectFmt,"

($[redirected from] {\$FullName})

\n"); if (@!$_GET['from']) { $opt['redirect'] = 1; $PageRedirectFmt = ''; } else { $frompage = MakePageName($pagename, $_GET['from']); $PageRedirectFmt = (!$frompage) ? '' : FmtPageName($PageRedirectFmt, $frompage); } if (@$EnableHTMLCache && !$NoHTMLCache && $PageCacheFile && @filemtime($PageCacheFile) > $LastModTime) { list($ctext) = unserialize(file_get_contents($PageCacheFile)); $FmtV['$PageText'] = "$ctext"; $IsHTMLCached = 1; StopWatch("HandleBrowse: using cached copy"); } else { $IsHTMLCached = 0; $text = '(:groupheader:)'.@$text.'(:groupfooter:)'; $t1 = time(); $FmtV['$PageText'] = MarkupToHTML($pagename, $text, $opt); if (@$EnableHTMLCache > 0 && !$NoHTMLCache && $PageCacheFile && (time() - $t1 + 1) >= $EnableHTMLCache) { $fp = @fopen("$PageCacheFile,new", "x"); if ($fp) { StopWatch("HandleBrowse: caching page"); fwrite($fp, serialize(array($FmtV['$PageText']))); fclose($fp); rename("$PageCacheFile,new", $PageCacheFile); } } } SDV($HandleBrowseFmt,array(&$PageStartFmt, &$PageRedirectFmt, '$PageText', &$PageEndFmt)); PrintFmt($pagename,$HandleBrowseFmt); } ## UpdatePage goes through all of the steps needed to update a page, ## preserving page history, computing link targets, page titles, ## and other page attributes. It does this by calling each entry ## in $EditFunctions. $pagename is the name of the page to be updated, ## $page is the old version of the page (used for page history), ## $new is the new version of the page to be saved, and $fnlist is ## an optional list of functions to use instead of $EditFunctions. function UpdatePage(&$pagename, &$page, &$new, $fnlist = NULL) { global $EditFunctions, $IsPagePosted; StopWatch("UpdatePage: begin $pagename"); if (is_null($fnlist)) $fnlist = $EditFunctions; $IsPagePosted = false; foreach((array)$fnlist as $fn) { StopWatch("UpdatePage: $fn ($pagename)"); $fn($pagename, $page, $new); } StopWatch("UpdatePage: end $pagename"); return $IsPagePosted; } # 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); $new['=preview'] = $new['text']; PCache($pagename, $new); return $new['text']; } ## ReplaceOnSave performs text replacements on the text being posted. ## Patterns held in $ROEPatterns are replaced on every edit request, ## patterns held in $ROSPatterns are replaced only when the page ## is being posted (as signaled by $EnablePost). function ReplaceOnSave($pagename,&$page,&$new) { global $EnablePost, $ROSPatterns, $ROEPatterns; $new['text'] = PPRA((array)@$ROEPatterns, $new['text']); if ($EnablePost) { $new['text'] = PPRA((array)@$ROSPatterns, $new['text']); } $new['=preview'] = $new['text']; PCache($pagename, $new); } function SaveAttributes($pagename,&$page,&$new) { global $EnablePost, $LinkTargets, $SaveAttrPatterns, $PCache, $SaveProperties; if (!$EnablePost) return; $text = PPRA($SaveAttrPatterns, $new['text']); $LinkTargets = array(); $html = MarkupToHTML($pagename,$text); $new['targets'] = implode(',',array_keys((array)$LinkTargets)); $p = & $PCache[$pagename]; foreach((array)$SaveProperties as $k) { if (@$p["=p_$k"]) $new[$k] = $p["=p_$k"]; else unset($new[$k]); } unset($new['excerpt']); } function PostPage($pagename, &$page, &$new) { global $DiffKeepDays, $DiffFunction, $DeleteKeyPattern, $EnablePost, $Now, $Charset, $Author, $WikiDir, $IsPagePosted, $DiffKeepNum; SDV($DiffKeepDays,3650); SDV($DiffKeepNum,20); SDV($DeleteKeyPattern,"^\\s*delete\\s*$"); $IsPagePosted = false; if ($EnablePost) { $new['charset'] = $Charset; # kept for now, may be needed if custom PageStore $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; $keepnum = array(); $keys = array_keys($new); foreach($keys as $k) if (preg_match("/^\\w+:(\\d+)/",$k,$match)) { $keepnum[$match[1]] = 1; if(count($keepnum)>$DiffKeepNum && $match[1]<$keepgmt) unset($new[$k]); } if (preg_match("/$DeleteKeyPattern/",$new['text'])){ if(@$new['passwdattr']>'' && !CondAuth($pagename, 'attr')) Abort('$[The page has an "attr" attribute and cannot be deleted.]'); else $WikiDir->delete($pagename); } else WritePage($pagename,$new); $IsPagePosted = true; } } function PostRecentChanges($pagename,$page,$new,$Fmt=null) { global $IsPagePosted, $RecentChangesFmt, $RCDelimPattern, $RCLinesMax; if (!$IsPagePosted && $Fmt==null) return; if ($Fmt==null) $Fmt = $RecentChangesFmt; foreach($Fmt 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("/^.*$rcelim.*\n/m", '', @$rcpage['text']); if (!preg_match("/$RCDelimPattern/",$rcpage['text'])) $rcpage['text'] .= "$pgtext\n"; else $rcpage['text'] = preg_replace("/([^\n]*$RCDelimPattern.*\n)/", str_replace("$", "\\$", $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 AutoCreateTargets($pagename, &$page, &$new) { global $IsPagePosted, $AutoCreate, $LinkTargets; if (!$IsPagePosted) return; foreach((array)@$AutoCreate as $pat => $init) { if (is_null($init)) continue; foreach(preg_grep($pat, array_keys((array)@$LinkTargets)) as $aname) { if (PageExists($aname)) continue; $x = RetrieveAuthPage($aname, 'edit', false, READPAGE_CURRENT); if (!$x) continue; WritePage($aname, $init); } } } function PreviewPage($pagename,&$page,&$new) { global $IsPageSaved, $FmtV; if (@$_REQUEST['preview']) { $text = '(:groupheader:)'.$new['text'].'(:groupfooter:)'; $FmtV['$PreviewText'] = MarkupToHTML($pagename,$text); } } function HandleEdit($pagename, $auth = 'edit') { global $IsPagePosted, $EditFields, $ChangeSummary, $EditFunctions, $EnablePost, $FmtV, $Now, $EditRedirectFmt, $PageEditForm, $HandleEditFmt, $PageStartFmt, $PageEditFmt, $PageEndFmt; SDV($EditRedirectFmt, '$FullName'); if (@$_POST['cancel']) { Redirect(FmtPageName($EditRedirectFmt, $pagename)); return; } Lock(2); $page = RetrieveAuthPage($pagename, $auth, true); if (!$page) Abort("?cannot edit $pagename"); $new = $page; foreach((array)$EditFields as $k) if (isset($_POST[$k])) $new[$k]=str_replace("\r",'',stripmagic($_POST[$k])); $new['csum'] = $ChangeSummary; if ($ChangeSummary) $new["csum:$Now"] = $ChangeSummary; $EnablePost &= preg_grep('/^post/', array_keys(@$_POST)); $new['=preview'] = $new['text']; PCache($pagename, $new); UpdatePage($pagename, $page, $new); Lock(0); if ($IsPagePosted && !@$_POST['postedit']) { Redirect(FmtPageName($EditRedirectFmt, $pagename)); return; } $FmtV['$DiffClassMinor'] = (@$_POST['diffclass']=='minor') ? "checked='checked'" : ''; $FmtV['$EditText'] = str_replace('$','$',PHSC(@$new['text'],ENT_NOQUOTES)); $FmtV['$EditBaseTime'] = $Now; if (@$PageEditForm) { $efpage = FmtPageName($PageEditForm, $pagename); $form = RetrieveAuthPage($efpage, 'read', false, READPAGE_CURRENT); if (!$form || !@$form['text']) Abort("?unable to retrieve edit form $efpage", 'editform'); $FmtV['$EditForm'] = MarkupToHTML($pagename, $form['text']); } SDV($PageEditFmt, "

$[Editing {\$FullName}]

\$EditMessageFmt
"); SDV($HandleEditFmt, array(&$PageStartFmt, &$PageEditFmt, &$PageEndFmt)); PrintFmt($pagename, $HandleEditFmt); } function HandleSource($pagename, $auth = 'read') { global $HTTPHeaders; $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT); if (!$page) Abort("?cannot source $pagename"); foreach ($HTTPHeaders as $h) { $h = preg_replace('!^Content-type:\\s+text/html!i', 'Content-type: text/plain', $h); header($h); } echo @$page['text']; } ## PmWikiAuth provides password-protection of pages using PHP sessions. ## It is normally called from RetrieveAuthPage. Since RetrieveAuthPage ## can be called a lot within a single page execution (i.e., for every ## page accessed), we cache the results of site passwords and ## GroupAttribute pages to be able to speed up subsequent calls. function PmWikiAuth($pagename, $level, $authprompt=true, $since=0) { global $DefaultPasswords, $GroupAttributesFmt, $AllowPassword, $AuthCascade, $FmtV, $AuthPromptFmt, $PageStartFmt, $PageEndFmt, $AuthId, $AuthList, $NoHTMLCache; static $acache; SDV($GroupAttributesFmt,'$Group/GroupAttributes'); SDV($AllowPassword,'nopass'); $page = ReadPage($pagename, $since); if (!$page) { return false; } if (!isset($acache)) SessionAuth($pagename, (@$_POST['authpw']) ? array('authpw' => array($_POST['authpw'] => 1)) : ''); if (@$AuthId) { $AuthList["id:$AuthId"] = 1; $AuthList["id:-$AuthId"] = -1; $AuthList["id:*"] = 1; } ## To allow @_site_edit in GroupAttributes, we cache it first if (!isset($acache['@site'])) { foreach($DefaultPasswords as $k => $v) { $x = array(2, array(), ''); $acache['@site'][$k] = IsAuthorized($v, 'site', $x); $AuthList["@_site_$k"] = $acache['@site'][$k][0] ? 1 : 0; } } $gn = FmtPageName($GroupAttributesFmt, $pagename); if (!isset($acache[$gn])) { $gp = ReadPage($gn, READPAGE_CURRENT); foreach($DefaultPasswords as $k => $v) { $acache[$gn][$k] = IsAuthorized(@$gp["passwd$k"], 'group', $acache['@site'][$k]); } } foreach($DefaultPasswords as $k => $v) list($page['=auth'][$k], $page['=passwd'][$k], $page['=pwsource'][$k]) = IsAuthorized(@$page["passwd$k"], 'page', $acache[$gn][$k]); foreach($AuthCascade as $k => $t) { if ($page['=auth'][$k]+0 == 2) { $page['=auth'][$k] = $page['=auth'][$t]; if ($page['=passwd'][$k] = $page['=passwd'][$t]) # assign $page['=pwsource'][$k] = "cascade:$t"; } } if (@$page['=auth']['admin']) foreach($page['=auth'] as $lv=>$a) @$page['=auth'][$lv] = 3; if (@$page['=passwd']['read']) $NoHTMLCache |= 2; if ($level=='ALWAYS' || @$page['=auth'][$level]) return $page; if (!$authprompt) return false; $GLOBALS['AuthNeeded'] = (@$_POST['authpw']) ? $page['=pwsource'][$level] . ' ' . $level : ''; PCache($pagename, $page); $postvars = ''; foreach($_POST as $k=>$v) { if ($k == 'authpw' || $k == 'authid') continue; $k = PHSC(stripmagic($k), ENT_QUOTES); $v = str_replace('$', '$', PHSC(stripmagic($v), ENT_COMPAT)); $postvars .= "\n"; } $FmtV['$PostVars'] = $postvars; $r = str_replace("'", '%37', stripmagic($_SERVER['REQUEST_URI'])); SDV($AuthPromptFmt,array(&$PageStartFmt, "

$[Password required]

$[Password]: \$PostVars
", &$PageEndFmt)); PrintFmt($pagename,$AuthPromptFmt); exit; } function IsAuthorized($chal, $source, &$from) { global $AuthList, $AuthPw, $AllowPassword; if (!$chal) return $from; $auth = 0; $passwd = array(); foreach((array)$chal as $c) { $x = ''; $pwchal = preg_split('/([, ]|\\w+:)/', $c, -1, PREG_SPLIT_DELIM_CAPTURE); foreach($pwchal as $pw) { if ($pw == ',' || $pw == '') continue; else if ($pw == ' ') { $x = ''; continue; } else if (substr($pw, -1, 1) == ':') { $x = $pw; continue; } else if ($pw{0} != '@' && $x > '') $pw = $x . $pw; if (!$pw) continue; $passwd[] = $pw; if ($auth < 0) continue; if ($x || $pw{0} == '@') { if (@$AuthList[$pw]) $auth = $AuthList[$pw]; continue; } if (crypt($AllowPassword, $pw) == $pw) # nopass { $auth=1; continue; } foreach((array)$AuthPw as $pwresp) # password if (crypt($pwresp, $pw) == $pw) { $auth=1; continue; } } } if (!$passwd) return $from; if ($auth < 0) $auth = 0; return array($auth, $passwd, $source); } ## SessionAuth works with PmWikiAuth to manage authorizations ## as stored in sessions. First, it can be used to set session ## variables by calling it with an $auth argument. It then ## uses the authid, authpw, and authlist session variables ## to set the corresponding values of $AuthId, $AuthPw, and $AuthList ## as needed. function SessionAuth($pagename, $auth = NULL) { global $AuthId, $AuthList, $AuthPw, $SessionEncode, $SessionDecode, $EnableSessionPasswords; static $called; @$called++; $sn = session_name(); # in PHP5.3, $_REQUEST doesn't contain $_COOKIE if (!$auth && ($called > 1 || (!@$_REQUEST[$sn] && !@$_COOKIE[$sn]))) return; $sid = session_id(); @session_start(); foreach((array)$auth as $k => $v) { if ($k == 'authpw') { foreach((array)$v as $pw => $pv) { if ($SessionEncode) $pw = $SessionEncode($pw); $_SESSION[$k][$pw] = $pv; } } else if ($k) $_SESSION[$k] = (array)$v + (array)@$_SESSION[$k]; } if (!isset($AuthId)) $AuthId = @end($_SESSION['authid']); $AuthPw = array_map($SessionDecode, array_keys((array)@$_SESSION['authpw'])); if (!IsEnabled($EnableSessionPasswords, 1)) $_SESSION['authpw'] = array(); $AuthList = array_merge($AuthList, (array)@$_SESSION['authlist']); if (!$sid) @session_write_close(); } function PasswdVar($pagename, $level) { global $PCache, $PasswdVarAuth, $FmtV; $page = $PCache[$pagename]; if (!isset($page['=passwd'][$level])) { $page = RetrieveAuthPage($pagename, 'ALWAYS', false, READPAGE_CURRENT); if ($page) PCache($pagename, $page); } SDV($PasswdVarAuth, 'attr'); if ($PasswdVarAuth && !@$page['=auth'][$PasswdVarAuth]) return XL('(protected)'); $pwsource = $page['=pwsource'][$level]; if (strncmp($pwsource, 'cascade:', 8) == 0) { $FmtV['$PWCascade'] = substr($pwsource, 8); return FmtPageName('$[(using $PWCascade password)]', $pagename); } $setting = PHSC(implode(' ', preg_replace('/^(?!@|\\w+:).+$/', '****', (array)$page['=passwd'][$level]))); if ($pwsource == 'group' || $pwsource == 'site') { $FmtV['$PWSource'] = $pwsource; $setting = FmtPageName('$[(set by $PWSource)] ', $pagename) . PHSC($setting); } return $setting; } function PrintAttrForm($pagename) { global $PageAttributes, $PCache, $FmtV; echo FmtPageName("
",$pagename); $page = $PCache[$pagename]; foreach($PageAttributes as $attr=>$p) { if (!$p) continue; if (strncmp($attr, 'passwd', 6) == 0) { $setting = PageVar($pagename, '$Passwd'.ucfirst(substr($attr, 6))); $value = ''; } else { $setting = $value = PHSC(@$page[$attr]); } $prompt = FmtPageName($p,$pagename); echo ""; } echo FmtPageName("
$prompt $setting
", $pagename); } function HandleAttr($pagename, $auth = 'attr') { global $HandleAttrFmt,$PageAttrFmt,$PageStartFmt,$PageEndFmt; $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT); if (!$page) { Abort("?unable to read $pagename"); } PCache($pagename,$page); XLSDV('en', array('EnterAttributes' => "Enter new attributes for this page below. Leaving a field blank will leave the attribute unchanged. To clear an attribute, enter 'clear'.")); SDV($PageAttrFmt,"

$[{\$FullName} Attributes]

$[EnterAttributes]

"); SDV($HandleAttrFmt,array(&$PageStartFmt,&$PageAttrFmt, 'function:PrintAttrForm',&$PageEndFmt)); PrintFmt($pagename,$HandleAttrFmt); } function HandlePostAttr($pagename, $auth = 'attr') { global $PageAttributes, $EnablePostAttrClearSession; Lock(2); $page = RetrieveAuthPage($pagename, $auth, true); if (!$page) { Abort("?unable to read $pagename"); } foreach($PageAttributes as $attr=>$p) { $v = stripmagic(@$_POST[$attr]); if ($v == '') continue; if ($v=='clear') unset($page[$attr]); else if (strncmp($attr, 'passwd', 6) != 0) $page[$attr] = $v; else { $a = array(); preg_match_all('/"[^"]*"|\'[^\']*\'|\\S+/', $v, $match); foreach($match[0] as $pw) $a[] = preg_match('/^(@|\\w+:)/', $pw) ? $pw : crypt(preg_replace('/^([\'"])(.*)\\1$/', '$2', $pw)); if ($a) $page[$attr] = implode(' ',$a); } } WritePage($pagename,$page); Lock(0); if (IsEnabled($EnablePostAttrClearSession, 1)) { @session_start(); unset($_SESSION['authid']); unset($_SESSION['authlist']); $_SESSION['authpw'] = array(); } Redirect($pagename); exit; } function HandleLogoutA($pagename, $auth = 'read') { global $LogoutRedirectFmt, $LogoutCookies; SDV($LogoutRedirectFmt, '$FullName'); SDV($LogoutCookies, array()); @session_start(); $_SESSION = array(); if ( session_id() != '' || isset($_COOKIE[session_name()]) ) setcookie(session_name(), '', time()-43200, '/'); foreach ($LogoutCookies as $c) if (isset($_COOKIE[$c])) setcookie($c, '', time()-43200, '/'); session_destroy(); Redirect(FmtPageName($LogoutRedirectFmt, $pagename)); } function HandleLoginA($pagename, $auth = 'login') { global $AuthId, $DefaultPasswords; unset($DefaultPasswords['admin']); $prompt = @(!$_POST['authpw'] || ($AuthId != $_POST['authid'])); $page = RetrieveAuthPage($pagename, $auth, $prompt, READPAGE_CURRENT); Redirect($pagename); }