with your question(s) and I'll provide explanations (and add comments) that answer them. Script maintained by Petko Yotov www.pmwiki.org/petko $Id$ */ 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(); $FmtV['$TokenName'] = 'pmtoken'; define('PmWiki',1); SDV($WorkDir,'wiki.d'); SDV($FarmD,dirname(__FILE__)); if (preg_match('!^phar://[^:]*$!', $FarmD)) { $IsPmArchive = 1; SDV($PmArchiveDir, substr(dirname(dirname($FarmD)), 7)); } elseif (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', 'MergeLastMinorEdit', 'SaveChangeSummary', '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}]] . . . $CurrentLocalTime $[by] $AuthorLink: [=$ChangeSummary=]', '$Group.RecentChanges' => '* [[{$Group}/{$Name}]] . . . $CurrentLocalTime $[by] $AuthorLink: [=$ChangeSummary=]'); $UrlScheme = (@$_SERVER['HTTPS']=='on' || @$_SERVER['SERVER_PORT']==443) ? 'https' : 'http'; $ScriptUrl = $UrlScheme.'://'.strval(@$_SERVER['HTTP_HOST']).strval(@$_SERVER['SCRIPT_NAME']); $PubDirUrl = preg_replace('#/[^/]*$#', '/pub', $ScriptUrl, 1); SDV($FarmPubDirPrefix, 'PmFarmPubDirUrl'); if (@$IsPmArchive) SDV($FarmPubDirUrl, "$ScriptUrl/$FarmPubDirPrefix"); $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' => 'PSFT($GLOBALS["TimeFmt"], $page["time"])', '$LastModifiedSummary' => '@$page["csum"]', '$LastModifiedTime' => '$page["time"]', '$Description' => '@$page["description"]', '$SiteGroup' => '$GLOBALS["SiteGroup"]', '$SiteAdminGroup' => '$GLOBALS["SiteAdminGroup"]', '$VersionNum' => '$GLOBALS["VersionNum"]', '$Version' => '$GLOBALS["Version"]', '$WikiTitle' => '$GLOBALS["WikiTitle"]', '$PageLogoUrl' => 'strval(@$GLOBALS["PageLogoUrl"])', '$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")', '$EnabledIMap' => 'implode("|", array_keys($GLOBALS["IMap"]))', # PmSyntax '$GroupHomePage' => 'FmtGroupHome($pn,$group,$var)', '$GroupHomePageName' => 'FmtGroupHome($pn,$group,$var)', '$GroupHomePageTitle' => 'FmtGroupHome($pn,$group,$var)', '$GroupHomePageTitlespaced' => 'FmtGroupHome($pn,$group,$var)', '$GroupHomePageUrl' => 'FmtGroupHome($pn,$group,$var)', ); $SaveProperties = array('title', 'description', 'keywords'); $PageTextVarPatterns = array( 'var:' => '/^(:*[ \\t]*(\\w[-\\w]*)[ \\t]*:[ \\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;"); $HTTPHeaders['XFO'] = 'X-Frame-Options: SAMEORIGIN'; $HTTPHeaders['CSP'] = "Content-Security-Policy: frame-ancestors 'self'; base-uri 'self'; object-src 'none';"; $HTTPHeaders['XSSP'] = 'X-XSS-Protection: 1; mode=block'; $CacheActions = array('browse','diff','print'); $EnableHTMLCache = 0; $NoHTMLCache = 0; $HTMLTagAttr = ''; $HTMLDoctypeFmt = " \n"; $HTMLStylesFmt['pmwiki'] = " ul, ol, pre, dl, p { margin-top:0px; margin-bottom:0px; } code.escaped { white-space: pre; } .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'=>'@lock','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['action'] = "(boolean)MatchNames(\$GLOBALS['action'], \$condparm, false)"; $Conditions['match'] = 'preg_match("!$condparm!",$pagename)'; $Conditions['authid'] = 'NoCache(@$GLOBALS["AuthId"] > "")'; $Conditions['equal'] = 'CompareArgs($condparm) == 0'; function CompareArgs($arg) { $arg = ParseArgs($arg); return strcmp(@$arg[''][0], @$arg[''][1]); } $Conditions['auth'] = 'NoCache(CondAuth($pagename, $condparm, 1))'; function CondAuth($pagename, $condparm, $use_cache = 0) { global $AuthList, $HandleAuth; static $ccache = []; @list($level, $pn) = explode(' ', $condparm, 2); if (@$level && $level[0] == '@') { # user belongs to @group1,@group2 $keys = MatchNames(array_keys((array)@$AuthList), $level, true); foreach($keys as $k) { if (@$AuthList[$k] == 1 && $AuthList["-$k"] != 1) return true; } return false; } $pn = ($pn > '') ? MakePageName($pagename, $pn) : $pagename; if (@$HandleAuth[$level]>'') $level = $HandleAuth[$level]; if ($use_cache) { if (!isset($ccache[$pn][$level])) { $ccache[$pn][$level] = (boolean)RAPC($pn, $level); } return $ccache[$pn][$level]; } return (boolean)RAPC($pn, $level); } $Conditions['exists'] = 'CondExists($condparm)'; ## This is an optimized version of the earlier conditional ## especially for pagelists function CondExists($condparm, $caseinsensitive = true) { static $ls = false; if (!$ls) $ls = ListPages(); $condparm = str_replace(array('[[',']]'), array('', ''), $condparm); $glob = FixGlob($condparm, '$1*.$2'); return (boolean)MatchPageNames($ls, $glob, $caseinsensitive); } ## 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'; } ## PITS:01480, (:if [ {$EmptyPV} and ... ]:) $code = trim(preg_replace('/(^\\s*|(and|x?or|&&|\\|\\||!)\\s+)(?=and|x?or|&&|\\|\\|)/', '$1 0 ', trim(implode(' ', $terms)))); if (!$code) return false; return @eval("return( $code );"); } $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('closeall', '_begin', '/^\\(:closeall:\\)$/', "MarkupMarkupClose"); function MarkupMarkupClose() { return '<:block>' . MarkupClose(); } $ImgExtPattern="\\.(?:gif|jpg|jpeg|a?png|svgz?|GIF|JPG|JPEG|A?PNG|SVGZ?|webp|WEBP|avifs?|AVIFS?)"; $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', strval(@$_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 = strval(@$_REQUEST['n']); if (!$pagename) $pagename = strval(@$_REQUEST['pagename']); if (!$pagename && preg_match('!^'.preg_quote(strval(@$_SERVER['SCRIPT_NAME']),'!').'/?([^?]*)!', strval(@$_SERVER['REQUEST_URI']),$match)) $pagename = urldecode($match[1]); if (preg_match('/[\\x80-\\xbf]/',$pagename)) $pagename=pm_recode($pagename, 'UTF-8', 'WINDOWS-1252'); $pagename = preg_replace('![^[:alnum:]\\x80-\\xff]+$!','',$pagename); $pagename_unfiltered = $pagename; $pagename = preg_replace('![${}\'"\\\\]+!', '', $pagename); $FmtPV['$RequestedPage'] = 'PHSC($GLOBALS["pagename_unfiltered"], ENT_QUOTES)'; $Cursor['*'] = &$pagename; if (function_exists("date_default_timezone_get") ) { # fix PHP5.3 warnings @date_default_timezone_set(@date_default_timezone_get()); } $DenyHtaccessContent = << Order Deny,Allow Deny from all Require all denied EOF; SDVA($ServeFileExts, array( 'gif' => 'image/gif', 'png' => 'image/png', 'svg' => 'image/svg+xml', 'README' => 'text/plain', 'txt' => 'text/plain', 'css' => 'text/css', 'js' => 'application/javascript', )); function pm_servefile($basedir, $path, $cachecontrol='no-cache') { global $ServeFileExts; header("X-Sent-Via: pm_servefile"); $ext = preg_replace('!^.*[./]!', '', $path); if (!isset($ServeFileExts[$ext]) || preg_match('/[?#${}]|\\.\\./', $path)) { http_response_code(403); die('Forbidden'); } $filepath = "$basedir/$path"; if (!file_exists($filepath)) { http_response_code(404); die('File not found'); } header("Cache-Control: $cachecontrol"); header('Expires: '); $filelastmod = gmdate('D, d M Y H:i:s \G\M\T', filemtime($filepath)); if (@$_SERVER['HTTP_IF_MODIFIED_SINCE'] == $filelastmod) { http_response_code(304); exit(); } header("Last-Modified: $filelastmod"); header("Content-Type: {$ServeFileExts[$ext]}"); $length = filesize($filepath); header("Content-Length: $length"); readfile($filepath); exit; } if (strpos($pagename, "$FarmPubDirPrefix/")===0) { $path = substr($pagename, strlen("$FarmPubDirPrefix/")); pm_servefile("$FarmD/pub", $path); exit; } $PostConfig = array(); if (file_exists("$FarmD/local/farmconfig.php")) include_once("$FarmD/local/farmconfig.php"); if (@$IsPmArchive && file_exists("$PmArchiveDir/local/farmconfig.php")) include_once("$PmArchiveDir/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, PSFT($TimeFmt, $Now)); SDV($CurrentLocalTime, PSFT("@$TimeISOZFmt", $Now, null, 'GMT')); SDV($CurrentTimeISO, PSFT($TimeISOFmt, $Now)); if (IsEnabled($EnableStdConfig,1)) include_once("$FarmD/scripts/stdconfig.php"); asort($PostConfig, SORT_NUMERIC); while(count($PostConfig)) { $k = array_keys($PostConfig)[0]; $v = array_shift($PostConfig); if (!$k || !$v || $v<50) continue; if (function_exists($k)) $k($pagename); elseif (file_exists($k)) include_once($k); } function pmsetcookie($name, $val="", $exp=0, $path="", $dom="", $secure=null, $httponly=null, $samesite=null) { global $EnableCookieSecure, $EnableCookieHTTPOnly, $SetCookieFunction, $CookieSameSite; if (IsEnabled($SetCookieFunction)) return $SetCookieFunction($name, $val, $exp, $path, $dom, $secure, $httponly, $samesite); if (is_null($secure)) $secure = IsEnabled($EnableCookieSecure, false); if (is_null($httponly)) $httponly = IsEnabled($EnableCookieHTTPOnly, false); if (is_null($samesite)) $samesite = IsEnabled($CookieSameSite, 'Lax'); if (PHP_VERSION_ID>=70300) { return setcookie($name, $val, array( 'expires' => $exp, 'path' => $path, 'domain' => $dom, 'secure' => $secure, 'httponly' => $httponly, 'samesite' => $samesite )); } if (!$path) $path = '/'; setcookie($name, $val, $exp, "$path; SameSite=$samesite", $dom, $secure, $httponly); } function pm_session_start($a = array()) { global $EnableCookieSecure, $EnableCookieHTTPOnly, $CookieSameSite; if (function_exists('session_status')) { if (session_status() === PHP_SESSION_ACTIVE) return true; } if (!headers_sent()){ $params = session_get_cookie_params(); if (isset($EnableCookieSecure) && !isset($a['secure'])) $a['secure'] = $EnableCookieSecure; if (isset($EnableCookieHTTPOnly) && !isset($a['httponly'])) $a['httponly'] = $EnableCookieHTTPOnly; if (!isset($a['samesite'])) $a['samesite'] = IsEnabled($CookieSameSite, 'Lax'); SDVA($a, $params); if (PHP_VERSION_ID < 70300) { if (!$a['path']) $a['path'] = '/'; $a['path'] .= "; SameSite={$a['samesite']}"; session_set_cookie_params( $a['lifetime'], $a['path'], $a['domain'], $a['secure'], $a['httponly'] ); } else { session_set_cookie_params($a); } } return @session_start(); } 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) { if (is_null($x)) return ''; $fn = 'get_magic_quotes_gpc'; if (!function_exists($fn)) return $x; if (is_array($x)) { foreach($x as $k=>$v) $x[$k] = stripmagic($v); return $x; } $x = strval($x); return @$fn() ? 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 $x? preg_replace_callback('/[\\x80-\\xff \'"<>]/', "cb_pue", $x) : ''; } function cb_pue($m) { return '%'.dechex(ord($m[0])); } function PQA($x, $keep=true, $styletoclass=false) { global $EnableUnsafeInlineStyle; if (!@$x) return ''; # PHP 8.1 $out = ''; $s = array(); $x = MarkupRestore($x); 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; $val = preg_replace('/^([\'"]?)(.*)\\1$/', '$2', $a[2]); $s[$a[1]] = $val; } foreach($s as $key=>$val) { $val = PHSC($val, ENT_QUOTES, null, false); if ($keep) $val = Keep($val); $out .= "$key='$val' "; } } 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+)/", strval($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 PosArgs($args, $posnames) { if (is_string($args)) $args = ParseArgs($args); if (!is_array($posnames)) { $posnames = array_slice(func_get_args(), 1); } while (count($posnames) > 0 && isset($args['']) && count($args['']) > 0) { $n = array_shift($posnames); if (!isset($args[$n])) $args[$n] = array_shift($args['']); } return $args; } function PHSC($x, $flags=ENT_COMPAT, $enc=null, $dbl_enc=true) { # for PHP 5.4 if (is_null($enc)) $enc = "ISO-8859-1"; # single-byte charset if (! is_array($x)) return @htmlspecialchars($x, $flags, $enc, $dbl_enc); foreach($x as $k=>$v) $x[$k] = PHSC($v, $flags, $enc, $dbl_enc); return $x; } function PSFT($fmt, $stamp=null, $locale=null, $tz=null) { # strftime() replacement global $Now, $FTimeFmt, $TimeFmt, $EnableFTimeNew; static $cached; if (!@$cached) { SDV($FTimeFmt, $TimeFmt); $cached['dloc'] = setlocale(LC_TIME, 0); $cached['dtz'] = date_default_timezone_get(); $cached['dtzo'] = timezone_open($cached['dtz']); $cached['formats'] = array( '%d' => 'd', '%e' => ' j', # day " 1"-"31" '%u' => 'N', '%w' => 'w', '%m' => 'm', '%s' => 'U', '%n' => "\n", '%t' => "\t", '%V' => 'W', # ISO-8601 week number, starts Monday, first week with 4+ weekdays '%H' => 'H', '%I' => 'h', # hour 01-12 '%k' => ' G', # hour " 1"-"23" '%l' => ' g', # hour " 1"-"12" '%M' => 'i', '%S' => 's', '%R' => 'H:i', '%T' => 'H:i:s', '%r' => 'h:i:s A', '%D' => 'm/d/y', '%F' => 'Y-m-d', '%G' => 'o', # ISO-8601 year (like %V) '%y' => 'y', # 21 '%Y' => 'Y', # 2021 '%Z' => 'T', # tz: EST '%z' => 'O', # tz offset: -0500 ); if (extension_loaded('intl')) { $full = IntlDateFormatter::FULL; $long = IntlDateFormatter::LONG; $short = IntlDateFormatter::SHORT; $none = IntlDateFormatter::NONE; $medium = IntlDateFormatter::MEDIUM; $cached['iformats'] = array( '%a' => array($full, $full, 'EEE'), # Mon '%A' => array($full, $full, 'EEEE'), # Monday '%h' => array($full, $full, 'MMM'), # Jan '%b' => array($full, $full, 'MMM'), # Jan '%B' => array($full, $full, 'MMMM'), # January '%p' => array($full, $full, 'aa'), # AM/PM '%P' => array($full, $full, 'aa'), # am/pm (no lowercase for intl am/pm) '%c' => array($long, $short), # date time '%x' => array($short, $none), # date '%X' => array($none, $medium),# time ); } else { $cached['iformats'] = array(); $cached['formats'] += array( '%a' => 'D', # Mon '%A' => 'l', # Monday '%h' => 'M', # Jan '%b' => 'M', # Jan '%B' => 'F', # January '%p' => 'a', # AM/PM '%P' => 'A', # am/pm '%c' => 'D M j H:i:s Y', # date time '%x' => 'm/d/y', # date '%X' => 'H:i:s', # time ); } $cached['new'] = isset($EnableFTimeNew) ? $EnableFTimeNew : (PHP_VERSION_ID>=80100); } if (@$fmt == '') $fmt = $FTimeFmt; $stamp = is_numeric($stamp)? intval($stamp) : $Now; if (preg_match('/(? $tz, 'timestamp' => $timestamp, 'formats' => $cached['formats'], 'iformats' => $cached['iformats'], ); if ($locale) $vars['locale'] = substr($locale, 0, 5); $cb = new PPRC($vars); $fmt = preg_replace_callback('/(?format($formats[$fmt]); if ($fmt[0]==' ' && strlen($fmt)>2) $fmt = substr($fmt, 1); return $fmt; } if ($fmt=='%o') { # ordinal, PITS:01418 if (!@$locale) $locale = 'C'; $f = numfmt_create($locale, NumberFormatter::ORDINAL); $o = $f->format($timestamp->format('j')); return preg_replace('/\\d+/', '', $o); } if ($fmt=='%j') return sprintf('%03d',$timestamp->format('z')+1); if ($fmt=='%C') return floor($timestamp->format('Y')/100); if ($fmt=='%g') return sprintf('%02d', $timestamp->format('o') % 100); if ($fmt=='%U') return cb_PSFT_UW($timestamp, $tz, 'Sunday'); if ($fmt=='%W') return cb_PSFT_UW($timestamp, $tz, 'Monday'); if (isset($iformats[$fmt])) { $ifmt = $iformats[$fmt]; $dfmt = datefmt_create(@$locale, $ifmt[0], $ifmt[1], $tz, null, @$ifmt[2]); if ($dfmt) return $dfmt->format($timestamp); } return $fmt; } function cb_PSFT_UW($timestamp, $tz, $day) { # helper for %U %W $first = strtotime(sprintf("%d-01 $day", $timestamp->format('Y'))); $stamp1 = date_create("@$first"); date_timezone_set($stamp1, timezone_open($tz)); $days = $timestamp->format('z') - $stamp1->format('z'); return sprintf('%02d', floor($days/7)+1); } # Only called by old addon|skin|recipe needing update, see pmwiki.org/Troubleshooting function PCCF($code, $template = 'default', $args = '$m') { global $CallbackFnTemplates, $CallbackFunctions, $PCCFOverrideFunction; if ($PCCFOverrideFunction && is_callable($PCCFOverrideFunction)) return $PCCFOverrideFunction($code, $template, $args); if (!isset($CallbackFnTemplates[$template])) Abort("No \$CallbackFnTemplates[$template])."); $code = sprintf($CallbackFnTemplates[$template], $code); if (!isset($CallbackFunctions[$code])) { $fn = create_function($args, $code); # called by old addon|skin|recipe needing update, see pmwiki.org/Troubleshooting if ($fn) $CallbackFunctions[$code] = $fn; else StopWatch("Failed to create callback function: ".PHSC($code)); } return $CallbackFunctions[$code]; } function PPRE($pat, $rep, $x) { if (! function_exists('create_function')) return $x; $lambda = PCCF("return $rep;"); return preg_replace_callback($pat, $lambda, $x); } function PPRA($array, $x) { if ($x==='' || is_null($x)) return ''; foreach((array)$array as $pat => $rep) { # skip broken patterns rather than crash the PHP installation $oldpat = preg_match('!^/.+/[^/]*e[^/]*$!', $pat); if ($oldpat && PHP_VERSION_ID >= 50500) continue; $fmt = $x; # for $FmtP if (is_callable($rep) && $rep != '_') $x = preg_replace_callback($pat,$rep,$x); else $x = preg_replace($pat,$rep,$x);# simple text OR called by old addon|skin|recipe needing update, see pmwiki.org/Troubleshooting } return $x; } function PRCB($pat, $repl, $subj, $vars=null, $limit=-1, &$count=null, $flags=0) { if (isset($vars)) { $cb = new PPRC($vars, $repl); $repl = array($cb, 'callback'); } if (PHP_VERSION_ID >= 70400) return preg_replace_callback($pat, $repl, $subj, $limit, $count, $flags); return preg_replace_callback($pat, $repl, $subj, $limit, $count); } function PRI($glue, $array) { ## Recursive implode $out = array(); foreach($array as $v) $out[] = is_array($v)? PRI($glue, $v) : $v; return implode($glue, $out); } ## This is a replacement for json_encode+PHSC, but only for arrays that ## are used by the PmWiki core. It may or may not work in other cases. ## This may fail with international characters if UTF-8 is not enabled. function pm_json_encode($x, $encodespecial=false) { if (!isset($x) || is_null($x)) return 'null'; if (is_bool($x)) return $x? "true" : "false"; if (is_int($x) || is_float($x)) return strval($x); if (function_exists('json_encode')) $out = json_encode($x); elseif (is_string($x)) ## escape controls and specials per RFC:8259 $out = '"'.preg_replace_callback("/[\x00-\x1f\\/\\\\\"]/",'cb_rfc8259',$x).'"'; elseif (is_array($x)) { $a = array(); if (array_values($x) === $x) { # numeric sequential array foreach($x as $v) $a[] = pm_json_encode($v); $out = "[".implode(',', $a)."]"; } else { # associative array -> json object foreach($x as $k=>$v) { $jk = is_int($k)? "\"$k\"" : pm_json_encode($k); $jv = pm_json_encode($v); $a[] = "$jk:$jv"; } $out = "{".implode(',', $a)."}"; } } else return 'null'; # other types not yet supported return $encodespecial? PHSC($out, ENT_QUOTES) : $out; } function cb_rfc8259($m) { return sprintf('\\u00%02x', ord($m[0])); } ## callback functions class PPRC { # PmWiki preg replace callbacks + pass local vars var $vars; var $cb; function __construct($vars = null, $cb = null) { if (!is_null($vars)) $this->vars = $vars; if (!is_null($cb)) $this->cb = $cb; } function pagevar($m) { # called from FmtPageName $pagename = $this->vars; return PageVar($pagename, $m[1]); } function ftime($m) { # called from PSFT $vars = $this->vars; return cb_PSFT($m[0], $vars); } function callback($m) { $cb = $this->cb; return $cb($m, $this->vars); } } # restores kept/protected strings function cb_expandkpv($m) { return @$GLOBALS['KPV'][$m[1]]; } # make a string upper or lower case in various patterns function cb_toupper($m) { return strtoupper($m[1]); } function cb_tolower($m) { return strtolower($m[1]); } function pmcrypt($str, $salt=null) { global $PmCryptAlgo; SDV($PmCryptAlgo, PASSWORD_DEFAULT); if ($salt && preg_match('/^(-?@|\\*$)/', $salt)) return false; if (!is_null($salt)) { if (function_exists('password_verify')) { if (password_verify($str, $salt)) return $salt; # else retry with crypt() } return crypt($str, $salt); } if (function_exists('password_hash')) return password_hash($str, $PmCryptAlgo); return crypt($str); } # generate or check a random session token to prevent CSRF # 0=get/set token, 1=check $_POST, 2=check $_GET, # 3=check $_COOKIE, -3=set $_COOKIE function pmtoken($check = 0, $abort = 0) { global $EnablePmToken, $PmTokenFn, $FmtV, $InputValues; static $called = 0, $cookie = 0; if (IsEnabled($PmTokenFn) && function_exists($PmTokenFn)) return $PmTokenFn($token); if (! IsEnabled($EnablePmToken, 1)) return true; pm_session_start(); $key = $FmtV['$TokenName']; if (!isset($_SESSION['pmtoken'])) { $token = $_SESSION['pmtoken'] = PmNonce().PmNonce(); } else $token = $_SESSION['pmtoken']; if (! $called++) { $InputValues[$key] = $FmtV['$TokenValue'] = $token; } if ($check == -3 && !$cookie++) pmsetcookie($key, $token); if ($check <= 0) { # get the token return $token; } # else: check the token if ($check === 1 && $token === @$_POST[$key]) return true; elseif ($check === 2 && $token === @$_GET[$key]) return true; elseif ($check === 3 && $token === @$_COOKIE[$key]) return true; # fail if ($abort) Abort('? $[Token invalid or missing.]'); return false; } function PmNonce() { if (function_exists('random_bytes')) { $nonce = bin2hex(random_bytes(5)); } else $nonce = 'x'.mt_rand().mt_rand(); return $nonce; } 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); } ## FmtDateTimeZ converts a GMT datetime like @2022-01-08T10:07:08Z ## into a