pmwiki.php 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721
  1. <?php
  2. /*
  3. PmWiki
  4. Copyright 2001-2006 Patrick R. Michaud
  5. pmichaud@pobox.com
  6. http://www.pmichaud.com/
  7. This program is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2 of the License, or
  10. (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. ----
  19. Note from Pm: Trying to understand the PmWiki code? Wish it had
  20. more comments? If you want help with any of the code here,
  21. write me at <pmichaud@pobox.com> with your question(s) and I'll
  22. provide explanations (and add comments) that answer them.
  23. */
  24. error_reporting(E_ALL ^ E_NOTICE);
  25. StopWatch('PmWiki');
  26. @ini_set('magic_quotes_runtime', 0);
  27. @ini_set('magic_quotes_sybase', 0);
  28. if (ini_get('register_globals'))
  29. foreach($_REQUEST as $k=>$v) {
  30. if (preg_match('/^(GLOBALS|_SERVER|_GET|_POST|_COOKIE|_FILES|_ENV|_REQUEST|_SESSION|FarmD|WikiDir)$/i', $k)) exit();
  31. ${$k}=''; unset(${$k});
  32. }
  33. $UnsafeGlobals = array_keys($GLOBALS); $GCount=0; $FmtV=array();
  34. SDV($FarmD,dirname(__FILE__));
  35. SDV($WorkDir,'wiki.d');
  36. define('PmWiki',1);
  37. if (preg_match('/\\w\\w:/', $FarmD)) exit();
  38. @include_once("$FarmD/scripts/version.php");
  39. $GroupPattern = '[[:upper:]][\\w]*(?:-\\w+)*';
  40. $NamePattern = '[[:upper:]\\d][\\w]*(?:-\\w+)*';
  41. $BlockPattern = 'form|div|table|t[rdh]|p|[uo]l|d[ltd]|h[1-6r]|pre|blockquote';
  42. $WikiWordPattern = '[[:upper:]][[:alnum:]]*(?:[[:upper:]][[:lower:]0-9]|[[:lower:]0-9][[:upper:]])[[:alnum:]]*';
  43. $WikiDir = new PageStore('wiki.d/{$FullName}');
  44. $WikiLibDirs = array(&$WikiDir,new PageStore('$FarmD/wikilib.d/{$FullName}'));
  45. $LocalDir = 'local';
  46. $InterMapFiles = array("$FarmD/scripts/intermap.txt",
  47. "$FarmD/local/farmmap.txt", '$SiteGroup.InterMap', 'local/localmap.txt');
  48. $Newline = "\263"; # deprecated, 2.0.0
  49. $KeepToken = "\235\235";
  50. $Now=time();
  51. define('READPAGE_CURRENT', $Now+604800);
  52. $TimeFmt = '%B %d, %Y, at %I:%M %p';
  53. $MessagesFmt = array();
  54. $BlockMessageFmt = "<h3 class='wikimessage'>$[This post has been blocked by the administrator]</h3>";
  55. $EditFields = array('text');
  56. $EditFunctions = array('EditTemplate', 'RestorePage', 'ReplaceOnSave',
  57. 'SaveAttributes', 'PostPage', 'PostRecentChanges', 'PreviewPage');
  58. $EnablePost = 1;
  59. $ChangeSummary = substr(stripmagic(@$_REQUEST['csum']), 0, 100);
  60. $AsSpacedFunction = 'AsSpaced';
  61. $SpaceWikiWords = 0;
  62. $LinkWikiWords = 0;
  63. $RCDelimPattern = ' ';
  64. $RecentChangesFmt = array(
  65. '$SiteGroup.AllRecentChanges' =>
  66. '* [[{$Group}.{$Name}]] . . . $CurrentTime $[by] $AuthorLink: [=$ChangeSummary=]',
  67. '$Group.RecentChanges' =>
  68. '* [[{$Group}/{$Name}]] . . . $CurrentTime $[by] $AuthorLink: [=$ChangeSummary=]');
  69. $ScriptUrl = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];
  70. $PubDirUrl = preg_replace('#/[^/]*$#','/pub',$ScriptUrl,1);
  71. $HTMLVSpace = "<vspace>";
  72. $HTMLPNewline = '';
  73. $MarkupFrame = array();
  74. $MarkupFrameBase = array('cs' => array(), 'vs' => '', 'ref' => 0,
  75. 'closeall' => array(), 'is' => array(),
  76. 'escape' => 1);
  77. $WikiWordCountMax = 1000000;
  78. $WikiWordCount['PmWiki'] = 1;
  79. $UrlExcludeChars = '<>"{}|\\\\^`()[\\]\'';
  80. $QueryFragPattern = "[?#][^\\s$UrlExcludeChars]*";
  81. $SuffixPattern = '(?:-?[[:alnum:]]+)*';
  82. $LinkPageSelfFmt = "<a class='selflink' href='\$LinkUrl'>\$LinkText</a>";
  83. $LinkPageExistsFmt = "<a class='wikilink' href='\$LinkUrl'>\$LinkText</a>";
  84. $LinkPageCreateFmt =
  85. "<a class='createlinktext' rel='nofollow'
  86. href='{\$PageUrl}?action=edit'>\$LinkText</a><a rel='nofollow'
  87. class='createlink' href='{\$PageUrl}?action=edit'>?</a>";
  88. $UrlLinkFmt =
  89. "<a class='urllink' href='\$LinkUrl' rel='nofollow'>\$LinkText</a>";
  90. umask(002);
  91. $CookiePrefix = '';
  92. $SiteGroup = 'Site';
  93. $DefaultGroup = 'Main';
  94. $DefaultName = 'HomePage';
  95. $GroupHeaderFmt = '(:include {$Group}.GroupHeader self=0:)(:nl:)';
  96. $GroupFooterFmt = '(:nl:)(:include {$Group}.GroupFooter self=0:)';
  97. $PagePathFmt = array('{$Group}.$1','$1.$1','$1.{$DefaultName}');
  98. $PageAttributes = array(
  99. 'passwdread' => '$[Set new read password:]',
  100. 'passwdedit' => '$[Set new edit password:]',
  101. 'passwdattr' => '$[Set new attribute password:]');
  102. $XLLangs = array('en');
  103. if (preg_match('/^C$|\.UTF-?8/i',setlocale(LC_ALL,0)))
  104. setlocale(LC_ALL,'en_US');
  105. $FmtP = array();
  106. $FmtPV = array(
  107. # '$ScriptUrl' => 'PUE($ScriptUrl)', ## $ScriptUrl is special
  108. '$PageUrl' =>
  109. 'PUE(($EnablePathInfo)
  110. ? "$ScriptUrl/$group/$name"
  111. : "$ScriptUrl?n=$group.$name")',
  112. '$FullName' => '"$group.$name"',
  113. '$Groupspaced' => '$AsSpacedFunction($group)',
  114. '$Namespaced' => '$AsSpacedFunction($name)',
  115. '$Group' => '$group',
  116. '$Name' => '$name',
  117. '$Titlespaced' =>
  118. '@$page["title"] ? $page["title"] : $AsSpacedFunction($name)',
  119. '$Title' =>
  120. '@$page["title"] ? $page["title"] : ($GLOBALS["SpaceWikiWords"]
  121. ? $AsSpacedFunction($name) : $name)',
  122. '$LastModifiedBy' => '@$page["author"]',
  123. '$LastModifiedHost' => '@$page["host"]',
  124. '$LastModified' => 'strftime($GLOBALS["TimeFmt"], $page["time"])',
  125. '$LastModifiedSummary' => '@$page["csum"]',
  126. '$Description' => '@$page["description"]',
  127. '$SiteGroup' => '$GLOBALS["SiteGroup"]',
  128. '$VersionNum' => '$GLOBALS["VersionNum"]',
  129. '$Version' => '$GLOBALS["Version"]',
  130. '$Author' => 'NoCache($GLOBALS["Author"])',
  131. '$AuthId' => 'NoCache($GLOBALS["AuthId"])',
  132. '$DefaultGroup' => '$GLOBALS["DefaultGroup"]',
  133. '$DefaultName' => '$GLOBALS["DefaultName"]',
  134. '$Action' => '$GLOBALS["action"]',
  135. );
  136. $SaveProperties = array('title', 'description', 'keywords');
  137. $WikiTitle = 'PmWiki';
  138. $Charset = 'ISO-8859-1';
  139. $HTTPHeaders = array(
  140. "Expires: Tue, 01 Jan 2002 00:00:00 GMT",
  141. "Cache-Control: no-store, no-cache, must-revalidate",
  142. "Content-type: text/html; charset=ISO-8859-1;");
  143. $CacheActions = array('browse','diff','print');
  144. $EnableHTMLCache = 0;
  145. $NoHTMLCache = 0;
  146. $HTMLDoctypeFmt =
  147. "<!DOCTYPE html
  148. PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"
  149. \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
  150. <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'><head>\n";
  151. $HTMLStylesFmt['pmwiki'] = "
  152. ul, ol, pre, dl, p { margin-top:0px; margin-bottom:0px; }
  153. code.escaped { white-space: nowrap; }
  154. .vspace { margin-top:1.33em; }
  155. .indent { margin-left:40px; }
  156. .outdent { margin-left:40px; text-indent:-40px; }
  157. a.createlinktext { text-decoration:none; border-bottom:1px dotted gray; }
  158. a.createlink { text-decoration:none; position:relative; top:-0.5em;
  159. font-weight:bold; font-size:smaller; border-bottom:none; }
  160. img { border:0px; }
  161. ";
  162. $HTMLHeaderFmt['styles'] = array(
  163. "<style type='text/css'><!--",&$HTMLStylesFmt,"\n--></style>");
  164. $HTMLBodyFmt = "</head>\n<body>";
  165. $HTMLStartFmt = array('headers:',&$HTMLDoctypeFmt,&$HTMLHeaderFmt,
  166. &$HTMLBodyFmt);
  167. $HTMLEndFmt = "\n</body>\n</html>";
  168. $PageStartFmt = array(&$HTMLStartFmt,"\n<div id='contents'>\n");
  169. $PageEndFmt = array('</div>',&$HTMLEndFmt);
  170. $HandleActions = array(
  171. 'browse' => 'HandleBrowse', 'print' => 'HandleBrowse',
  172. 'edit' => 'HandleEdit', 'source' => 'HandleSource',
  173. 'attr' => 'HandleAttr', 'postattr' => 'HandlePostAttr',
  174. 'logout' => 'HandleLogoutA', 'login' => 'HandleLoginA');
  175. $HandleAuth = array(
  176. 'browse' => 'read', 'source' => 'read', 'print' => 'read',
  177. 'edit' => 'edit', 'attr' => 'attr', 'postattr' => 'attr',
  178. 'logout' => 'read', 'login' => 'login');
  179. $ActionTitleFmt = array(
  180. 'edit' => '| $[Edit]',
  181. 'attr' => '| $[Attributes]');
  182. $DefaultPasswords = array('admin'=>'*','read'=>'','edit'=>'','attr'=>'');
  183. $AuthCascade = array('edit'=>'read', 'attr'=>'edit');
  184. $AuthList = array('' => 1, 'nopass:' => 1, '@nopass' => 1);
  185. $Conditions['enabled'] = '(boolean)@$GLOBALS[$condparm]';
  186. $Conditions['false'] = 'false';
  187. $Conditions['true'] = 'true';
  188. $Conditions['group'] =
  189. "(boolean)MatchPageNames(\$pagename, FixGlob(\$condparm, '$1$2.*'))";
  190. $Conditions['name'] =
  191. "(boolean)MatchPageNames(\$pagename, FixGlob(\$condparm, '$1*.$2'))";
  192. $Conditions['match'] = 'preg_match("!$condparm!",$pagename)';
  193. $Conditions['authid'] = 'NoCache(@$GLOBALS["AuthId"] > "")';
  194. $Conditions['exists'] = 'PageExists(MakePageName(\$pagename, \$condparm))';
  195. $Conditions['equal'] = 'CompareArgs($condparm) == 0';
  196. function CompareArgs($arg)
  197. { $arg = ParseArgs($arg); return strcmp(@$arg[''][0], @$arg[''][1]); }
  198. $Conditions['auth'] = 'CondAuth($pagename, $condparm)';
  199. function CondAuth($pagename, $condparm) {
  200. NoCache();
  201. list($level, $pn) = explode(' ', $condparm, 2);
  202. $pn = ($pn > '') ? MakePageName($pagename, $pn) : $pagename;
  203. return (boolean)RetrieveAuthPage($pn, $level, false, READPAGE_CURRENT);
  204. }
  205. ## CondExpr handles complex conditions (expressions)
  206. ## Portions Copyright 2005 by D. Faure (dfaure@cpan.org)
  207. function CondExpr($pagename, $condname, $condparm) {
  208. global $CondExprOps;
  209. SDV($CondExprOps, 'and|x?or|&&|\\|\\||[!()]');
  210. if ($condname == '(' || $condname == '[')
  211. $condparm = preg_replace('/[\\]\\)]\\s*$/', '', $condparm);
  212. $condparm = str_replace('&amp;&amp;', '&&', $condparm);
  213. $terms = preg_split("/(?<!\\S)($CondExprOps)(?!\\S)/i", $condparm, -1,
  214. PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
  215. foreach($terms as $i => $t) {
  216. $t = trim($t);
  217. if (preg_match("/^($CondExprOps)$/i", $t)) continue;
  218. if ($t) $terms[$i] = CondText($pagename, "if $t", 'TRUE') ? '1' : '0';
  219. }
  220. return @eval('return(' . implode(' ', $terms) . ');');
  221. }
  222. $Conditions['expr'] = 'CondExpr($pagename, $condname, $condparm)';
  223. $Conditions['('] = 'CondExpr($pagename, $condname, $condparm)';
  224. $Conditions['['] = 'CondExpr($pagename, $condname, $condparm)';
  225. $MarkupTable['_begin']['seq'] = 'B';
  226. $MarkupTable['_end']['seq'] = 'E';
  227. Markup('fulltext','>_begin');
  228. Markup('split','>fulltext',"\n",
  229. '$RedoMarkupLine=1; return explode("\n",$x);');
  230. Markup('directives','>split');
  231. Markup('inline','>directives');
  232. Markup('links','>inline');
  233. Markup('block','>links');
  234. Markup('style','>block');
  235. Markup('closeall', '_begin',
  236. '/^\\(:closeall:\\)$/e',
  237. "'<:block>' . MarkupClose()");
  238. $ImgExtPattern="\\.(?:gif|jpg|jpeg|png|GIF|JPG|JPEG|PNG)";
  239. $ImgTagFmt="<img src='\$LinkUrl' alt='\$LinkAlt' title='\$LinkAlt' />";
  240. $BlockMarkups = array(
  241. 'block' => array('','','',0),
  242. 'ul' => array('<ul><li>','</li><li>','</li></ul>',1),
  243. 'dl' => array('<dl>','</dd>','</dd></dl>',1),
  244. 'ol' => array('<ol><li>','</li><li>','</li></ol>',1),
  245. 'p' => array('<p>','','</p>',0),
  246. 'indent' =>
  247. array("<div class='indent'>","</div><div class='indent'>",'</div>',1),
  248. 'outdent' =>
  249. array("<div class='outdent'>","</div><div class='outdent'>",'</div>',1),
  250. 'pre' => array('<pre>','','</pre>',0),
  251. 'table' => array("<table width='100%'>",'','</table>',0));
  252. foreach(array('http:','https:','mailto:','ftp:','news:','gopher:','nap:',
  253. 'file:') as $m)
  254. { $LinkFunctions[$m] = 'LinkIMap'; $IMap[$m]="$m$1"; }
  255. $LinkFunctions['<:page>'] = 'LinkPage';
  256. $q = preg_replace('/(\\?|%3f)([-\\w]+=)/', '&$2', @$_SERVER['QUERY_STRING']);
  257. if ($q != @$_SERVER['QUERY_STRING']) {
  258. unset($_GET);
  259. parse_str($q, $_GET);
  260. $_REQUEST = array_merge($_REQUEST, $_GET, $_POST);
  261. }
  262. if (isset($_GET['action'])) $action = $_GET['action'];
  263. elseif (isset($_POST['action'])) $action = $_POST['action'];
  264. else $action = 'browse';
  265. $pagename = $_REQUEST['n'];
  266. if (!$pagename) $pagename = $_REQUEST['pagename'];
  267. if (!$pagename &&
  268. preg_match('!^'.preg_quote($_SERVER['SCRIPT_NAME'],'!').'/?([^?]*)!',
  269. $_SERVER['REQUEST_URI'],$match))
  270. $pagename = urldecode($match[1]);
  271. if (preg_match('/[\\x80-\\xbf]/',$pagename))
  272. $pagename=utf8_decode($pagename);
  273. $pagename = preg_replace('![^[:alnum:]\\x80-\\xff]+$!','',$pagename);
  274. $FmtPV['$RequestedPage'] = "'".htmlspecialchars($pagename, ENT_QUOTES)."'";
  275. if (file_exists("$FarmD/local/farmconfig.php"))
  276. include_once("$FarmD/local/farmconfig.php");
  277. if (IsEnabled($EnableLocalConfig,1)) {
  278. if (file_exists("$LocalDir/config.php"))
  279. include_once("$LocalDir/config.php");
  280. elseif (file_exists('config.php'))
  281. include_once('config.php');
  282. }
  283. SDV($CurrentTime,strftime($TimeFmt,$Now));
  284. if (IsEnabled($EnableStdConfig,1))
  285. include_once("$FarmD/scripts/stdconfig.php");
  286. foreach((array)$InterMapFiles as $f) {
  287. $f = FmtPageName($f, $pagename);
  288. if (($v = @file($f)))
  289. $v = preg_replace('/^\\s*(?>\\w[-\\w]*)(?!:)/m', '$0:', implode('', $v));
  290. else if (PageExists($f)) {
  291. $p = ReadPage($f, READPAGE_CURRENT);
  292. $v = $p['text'];
  293. } else continue;
  294. if (!preg_match_all("/^\\s*(\\w[-\\w]*:)[^\\S\n]+(\\S*)/m", $v,
  295. $match, PREG_SET_ORDER)) continue;
  296. foreach($match as $m) {
  297. if (strpos($m[2], '$1') === false) $m[2] .= '$1';
  298. $LinkFunctions[$m[1]] = 'LinkIMap';
  299. $IMap[$m[1]] = FmtPageName($m[2], $pagename);
  300. }
  301. }
  302. $LinkPattern = implode('|',array_keys($LinkFunctions));
  303. SDV($LinkPageCreateSpaceFmt,$LinkPageCreateFmt);
  304. $ActionTitle = FmtPageName(@$ActionTitleFmt[$action],$pagename);
  305. if (!function_exists(@$HandleActions[$action])) $action='browse';
  306. SDV($HandleAuth[$action], 'read');
  307. $HandleActions[$action]($pagename, $HandleAuth[$action]);
  308. Lock(0);
  309. return;
  310. ## helper functions
  311. function stripmagic($x)
  312. { return get_magic_quotes_gpc() ? stripslashes($x) : $x; }
  313. function pre_r(&$x)
  314. { return '<pre>'.htmlspecialchars(print_r($x, true)).'</pre>'; }
  315. function PSS($x)
  316. { return str_replace('\\"','"',$x); }
  317. function PVS($x)
  318. { return preg_replace("/\n[^\\S\n]*(?=\n)/", "\n<:vspace>", $x); }
  319. function PZZ($x,$y='') { return ''; }
  320. function PRR($x=NULL)
  321. { if ($x || is_null($x)) $GLOBALS['RedoMarkupLine']++; return $x; }
  322. function PUE($x)
  323. { return preg_replace('/[\\x80-\\xff \'"]/e', "'%'.dechex(ord('$0'))", $x); }
  324. function PQA($x) {
  325. $out = '';
  326. if (preg_match_all('/([a-zA-Z]+)\\s*=\\s*("[^"]*"|\'[^\']*\'|\\S*)/',
  327. $x, $attr, PREG_SET_ORDER)) {
  328. foreach($attr as $a) {
  329. if (preg_match('/^on/i', $a[1])) continue;
  330. $out .= $a[1] . '='
  331. . preg_replace( '/^(?![\'"]).*$/e',
  332. "\"'\".str_replace(\"'\", '&#39;', PSS('$0')).\"'\"", $a[2])
  333. . ' ';
  334. }
  335. }
  336. return $out;
  337. }
  338. function SDV(&$v,$x) { if (!isset($v)) $v=$x; }
  339. function SDVA(&$var,$val)
  340. { foreach($val as $k=>$v) if (!isset($var[$k])) $var[$k]=$v; }
  341. function IsEnabled(&$var,$f=0)
  342. { return (isset($var)) ? $var : $f; }
  343. function SetTmplDisplay($var, $val)
  344. { NoCache(); $GLOBALS['TmplDisplay'][$var] = $val; }
  345. function NoCache($x = '') { $GLOBALS['NoHTMLCache'] |= 1; return $x; }
  346. function ParseArgs($x) {
  347. $z = array();
  348. preg_match_all('/([-+]|(?>(\\w+)[:=]))?("[^"]*"|\'[^\']*\'|\\S+)/',
  349. $x, $terms, PREG_SET_ORDER);
  350. foreach($terms as $t) {
  351. $v = preg_replace('/^([\'"])?(.*)\\1$/', '$2', $t[3]);
  352. if ($t[2]) { $z['#'][] = $t[2]; $z[$t[2]] = $v; }
  353. else { $z['#'][] = $t[1]; $z[$t[1]][] = $v; }
  354. $z['#'][] = $v;
  355. }
  356. return $z;
  357. }
  358. function StopWatch($x) {
  359. global $StopWatch, $EnableStopWatch;
  360. if (!$EnableStopWatch) return;
  361. static $wstart = 0, $ustart = 0;
  362. list($usec,$sec) = explode(' ',microtime());
  363. $wtime = ($sec+$usec);
  364. if (!$wstart) $wstart = $wtime;
  365. if ($EnableStopWatch != 2)
  366. { $StopWatch[] = sprintf("%05.2f %s", $wtime-$wstart, $x); return; }
  367. $dat = getrusage();
  368. $utime = ($dat['ru_utime.tv_sec']+$dat['ru_utime.tv_usec']/1000000);
  369. if (!$ustart) $ustart=$utime;
  370. $StopWatch[] =
  371. sprintf("%05.2f %05.2f %s", $wtime-$wstart, $utime-$ustart, $x);
  372. }
  373. ## AsSpaced converts a string with WikiWords into a spaced version
  374. ## of that string. (It can be overridden via $AsSpacedFunction.)
  375. function AsSpaced($text) {
  376. $text = preg_replace("/([[:lower:]\\d])([[:upper:]])/", '$1 $2', $text);
  377. $text = preg_replace('/(?<![-\\d])(\\d+( |$))/',' $1',$text);
  378. return preg_replace("/([[:upper:]])([[:upper:]][[:lower:]\\d])/",
  379. '$1 $2', $text);
  380. }
  381. ## Lock is used to make sure only one instance of PmWiki is running when
  382. ## files are being written. It does not "lock pages" for editing.
  383. function Lock($op) {
  384. global $WorkDir,$LockFile;
  385. SDV($LockFile,"$WorkDir/.flock");
  386. mkdirp(dirname($LockFile));
  387. static $lockfp,$curop;
  388. if (!$lockfp) $lockfp = @fopen($LockFile,"w");
  389. if (!$lockfp) {
  390. @unlink($LockFile);
  391. $lockfp = fopen($LockFile,"w") or
  392. Abort("Cannot acquire lockfile", "Lockfile");
  393. fixperms($LockFile);
  394. }
  395. if ($op<0) { flock($lockfp,LOCK_UN); fclose($lockfp); $lockfp=0; $curop=0; }
  396. elseif ($op==0) { flock($lockfp,LOCK_UN); $curop=0; }
  397. elseif ($op==1 && $curop<1)
  398. { session_write_close(); flock($lockfp,LOCK_SH); $curop=1; }
  399. elseif ($op==2 && $curop<2)
  400. { session_write_close(); flock($lockfp,LOCK_EX); $curop=2; }
  401. }
  402. ## mkdirp creates a directory and its parents as needed, and sets
  403. ## permissions accordingly.
  404. function mkdirp($dir) {
  405. global $ScriptUrl;
  406. if (file_exists($dir)) return;
  407. if (!file_exists(dirname($dir))) mkdirp(dirname($dir));
  408. if (mkdir($dir, 0777)) {
  409. fixperms($dir);
  410. if (@touch("$dir/xxx")) { unlink("$dir/xxx"); return; }
  411. rmdir($dir);
  412. }
  413. $parent = realpath(dirname($dir));
  414. $perms = decoct(fileperms($parent) & 03777);
  415. $msg = "PmWiki needs to have a writable <tt>$dir/</tt> directory
  416. before it can continue. You can create the directory manually
  417. by executing the following commands on your server:
  418. <pre> mkdir $parent/$dir\n chmod 777 $parent/$dir</pre>
  419. Then, <a href='{$ScriptUrl}'>reload this page</a>.";
  420. $safemode = ini_get('safe_mode');
  421. if (!$safemode) $msg .= "<br /><br />Or, for a slightly more
  422. secure installation, try executing <pre> chmod 2777 $parent</pre>
  423. on your server and following <a target='_blank' href='$ScriptUrl'>
  424. this link</a>. Afterwards you can restore the permissions to
  425. their current setting by executing <pre> chmod $perms $parent</pre>.";
  426. Abort($msg);
  427. }
  428. ## fixperms attempts to correct permissions on a file or directory
  429. ## so that both PmWiki and the account (current dir) owner can manipulate it
  430. function fixperms($fname, $add = 0) {
  431. clearstatcache();
  432. if (!file_exists($fname)) Abort('no such file');
  433. $bp = 0;
  434. if (fileowner($fname)!=@fileowner('.')) $bp = (is_dir($fname)) ? 007 : 006;
  435. if (filegroup($fname)==@filegroup('.')) $bp <<= 3;
  436. $bp |= $add;
  437. if ($bp && (fileperms($fname) & $bp) != $bp)
  438. @chmod($fname,fileperms($fname)|$bp);
  439. }
  440. ## MatchPageNames
  441. function MatchPageNames($pagelist, $pat) {
  442. $pagelist = (array)$pagelist;
  443. foreach((array)$pat as $p) {
  444. if (count($pagelist) < 1) break;
  445. switch ($p{0}) {
  446. case '/':
  447. $pagelist = preg_grep($p, $pagelist);
  448. continue;
  449. case '!':
  450. $pagelist = array_diff($pagelist, preg_grep($p, $pagelist));
  451. continue;
  452. default:
  453. $p = preg_quote($p, '/');
  454. $p = str_replace(array('/', '\\*', '\\?', '\\[', '\\]', '\\^'),
  455. array('.', '.*', '.', '[', ']', '^'), $p);
  456. $excl = array(); $incl = array();
  457. foreach(preg_split('/[\\s,]+/', $p, -1, PREG_SPLIT_NO_EMPTY) as $q) {
  458. if ($q{0} == '-' || $q{0} == '!') $excl[] = '^'.substr($q, 1).'$';
  459. else $incl[] = "^$q$";
  460. }
  461. if ($excl)
  462. $pagelist = array_diff($pagelist,
  463. preg_grep('/' . join('|', $excl) . '/i', $pagelist));
  464. if ($incl)
  465. $pagelist = preg_grep('/' . join('|', $incl) . '/i', $pagelist);
  466. }
  467. }
  468. return $pagelist;
  469. }
  470. function FixGlob($x, $rep = '$1*.$2') {
  471. return preg_replace('/([\\s,][-!]?)([^.\\s,]+)(?=[\\s,])/', $rep, " $x ");
  472. }
  473. ## ResolvePageName "normalizes" a pagename based on the current
  474. ## settings of $DefaultPage and $PagePathFmt. It's normally used
  475. ## during initialization to fix up any missing or partial pagenames.
  476. function ResolvePageName($pagename) {
  477. global $DefaultPage, $DefaultGroup, $DefaultName,
  478. $GroupPattern, $NamePattern, $EnableFixedUrlRedirect;
  479. SDV($DefaultPage, "$DefaultGroup.$DefaultName");
  480. $pagename = preg_replace('!([./][^./]+)\\.html$!', '$1', $pagename);
  481. if ($pagename == '') return $DefaultPage;
  482. $p = MakePageName($DefaultPage, $pagename);
  483. if (!preg_match("/^($GroupPattern)[.\\/]($NamePattern)$/i", $p))
  484. Abort("?invalid page name");
  485. if (preg_match("/^($GroupPattern)[.\\/]($NamePattern)$/i", $pagename))
  486. return $p;
  487. if (IsEnabled($EnableFixedUrlRedirect, 1)
  488. && $p && (PageExists($p) || preg_match('/[\\/.]/', $pagename)))
  489. { Redirect($p); exit(); }
  490. return MakePageName($DefaultPage, "$pagename.$pagename");
  491. }
  492. ## MakePageName is used to convert a string into a valid pagename.
  493. ## If no group is supplied, then it uses $PagePathFmt to look
  494. ## for the page in other groups, or else uses the group of the
  495. ## pagename passed as an argument.
  496. function MakePageName($basepage,$x) {
  497. global $MakePageNameFunction, $PageNameChars, $PagePathFmt,
  498. $MakePageNamePatterns;
  499. if (@$MakePageNameFunction) return $MakePageNameFunction($basepage,$x);
  500. SDV($PageNameChars,'-[:alnum:]');
  501. SDV($MakePageNamePatterns, array(
  502. "/'/" => '', # strip single-quotes
  503. "/[^$PageNameChars]+/" => ' ', # convert everything else to space
  504. "/((^|[^-\\w])\\w)/e" => "strtoupper('$1')",
  505. "/ /" => ''));
  506. $m = preg_split('/[.\\/]/', $x);
  507. if (count($m)<1 || count($m)>2 || $m[0]=='') return '';
  508. if ($m[1] > '') {
  509. $group = preg_replace(array_keys($MakePageNamePatterns),
  510. array_values($MakePageNamePatterns), $m[0]);
  511. $name = preg_replace(array_keys($MakePageNamePatterns),
  512. array_values($MakePageNamePatterns), $m[1]);
  513. return "$group.$name";
  514. }
  515. $name = preg_replace(array_keys($MakePageNamePatterns),
  516. array_values($MakePageNamePatterns), $m[0]);
  517. if (count($m)>1) { $basepage = "$name.$name"; }
  518. foreach((array)$PagePathFmt as $pg) {
  519. $pn = FmtPageName(str_replace('$1',$name,$pg),$basepage);
  520. if (PageExists($pn)) return $pn;
  521. }
  522. $group=preg_replace('/[\\/.].*$/','',$basepage);
  523. return "$group.$name";
  524. }
  525. ## PCache caches basic information about a page and its attributes--
  526. ## usually everything except page text and page history. This makes
  527. ## for quicker access to certain values in PageVar below.
  528. function PCache($pagename, $page) {
  529. global $PCache;
  530. foreach($page as $k=>$v)
  531. if ($k!='text' && strpos($k,':')===false) $PCache[$pagename][$k]=$v;
  532. }
  533. ## SetProperty saves a page property into $PCache. For convenience
  534. ## it returns the $value of the property just set. If $sep is supplied,
  535. ## then $value is appended to the current property (with $sep as
  536. ## as separator) instead of replacing it.
  537. function SetProperty($pagename, $prop, $value, $sep = NULL) {
  538. global $PCache, $KeepToken;
  539. NoCache();
  540. $prop = "=p_$prop";
  541. $value = preg_replace("/$KeepToken(\\d.*?)$KeepToken/e",
  542. "\$GLOBALS['KPV']['$1']", $value);
  543. if (!is_null($sep) && isset($PCache[$pagename][$prop]))
  544. $value = $PCache[$pagename][$prop] . $sep . $value;
  545. $PCache[$pagename][$prop] = $value;
  546. return $value;
  547. }
  548. function PageVar($pagename, $var, $pn = '') {
  549. global $Cursor, $PCache, $FmtPV, $AsSpacedFunction, $ScriptUrl,
  550. $EnablePathInfo;
  551. if ($var == '$ScriptUrl') return PUE($ScriptUrl);
  552. if ($pn) {
  553. $pn = isset($Cursor[$pn]) ? $Cursor[$pn] : MakePageName($pagename, $pn);
  554. } else $pn = $pagename;
  555. if ($pn == '') return '';
  556. if (preg_match('/^(.+)[.\\/]([^.\\/]+)$/', $pn, $match)
  557. && !isset($PCache[$pn]['time'])
  558. && (!@$FmtPV[$var] || strpos($FmtPV[$var], '$page') !== false))
  559. { $page = ReadPage($pn, READPAGE_CURRENT); PCache($pn, $page); }
  560. @list($d, $group, $name) = $match;
  561. $page = &$PCache[$pn];
  562. if (@$FmtPV[$var]) return eval("return ({$FmtPV[$var]});");
  563. return '';
  564. }
  565. ## FmtPageName handles $[internationalization] and $Variable
  566. ## substitutions in strings based on the $pagename argument.
  567. function FmtPageName($fmt, $pagename) {
  568. # Perform $-substitutions on $fmt relative to page given by $pagename
  569. global $GroupPattern, $NamePattern, $EnablePathInfo, $ScriptUrl,
  570. $GCount, $UnsafeGlobals, $FmtV, $FmtP, $FmtPV, $PCache, $AsSpacedFunction;
  571. if (strpos($fmt,'$')===false) return $fmt;
  572. $fmt = preg_replace('/\\$([A-Z]\\w*Fmt)\\b/e','$GLOBALS[\'$1\']',$fmt);
  573. $fmt = preg_replace('/\\$\\[(?>([^\\]]+))\\]/e',"XL(PSS('$1'))",$fmt);
  574. $fmt = str_replace('{$ScriptUrl}', '$ScriptUrl', $fmt);
  575. $fmt =
  576. preg_replace('/\\{(\\$[A-Z]\\w+)\\}/e', "PageVar(\$pagename, '$1')", $fmt);
  577. if (strpos($fmt,'$')===false) return $fmt;
  578. if ($FmtP) $fmt = preg_replace(array_keys($FmtP), array_values($FmtP), $fmt);
  579. static $pv, $pvpat;
  580. if ($pv != count($FmtPV)) {
  581. $pvpat = str_replace('$', '\\$', implode('|', array_keys($FmtPV)));
  582. $pv = count($FmtPV);
  583. }
  584. $fmt = preg_replace("/(?:$pvpat)\\b/e", "PageVar(\$pagename, '$0')", $fmt);
  585. $fmt = preg_replace('!\\$ScriptUrl/([^?#\'"\\s<>]+)!e',
  586. (@$EnablePathInfo) ? "'$ScriptUrl/'.PUE('$1')" :
  587. "'$ScriptUrl?n='.str_replace('/','.',PUE('$1'))",
  588. $fmt);
  589. if (strpos($fmt,'$')===false) return $fmt;
  590. static $g;
  591. if ($GCount != count($GLOBALS)+count($FmtV)) {
  592. $g = array();
  593. foreach($GLOBALS as $n=>$v) {
  594. if (is_array($v) || is_object($v) ||
  595. isset($FmtV["\$$n"]) || in_array($n,$UnsafeGlobals)) continue;
  596. $g["\$$n"] = $v;
  597. }
  598. $GCount = count($GLOBALS)+count($FmtV);
  599. krsort($g); reset($g);
  600. }
  601. $fmt = str_replace(array_keys($g),array_values($g),$fmt);
  602. $fmt = preg_replace('/(?>(\\$[[:alpha:]]\\w+))/e',
  603. "isset(\$FmtV['$1']) ? \$FmtV['$1'] : '$1'", $fmt);
  604. return $fmt;
  605. }
  606. ## The XL functions provide translation tables for $[i18n] strings
  607. ## in FmtPageName().
  608. function XL($key) {
  609. global $XL,$XLLangs;
  610. foreach($XLLangs as $l) if (isset($XL[$l][$key])) return $XL[$l][$key];
  611. return $key;
  612. }
  613. function XLSDV($lang,$a) {
  614. global $XL;
  615. foreach($a as $k=>$v) { if (!isset($XL[$lang][$k])) $XL[$lang][$k]=$v; }
  616. }
  617. function XLPage($lang,$p) {
  618. global $TimeFmt,$XLLangs,$FarmD;
  619. $page = ReadPage($p, READPAGE_CURRENT);
  620. if (!$page) return;
  621. $text = preg_replace("/=>\\s*\n/",'=> ',@$page['text']);
  622. foreach(explode("\n",$text) as $l)
  623. if (preg_match('/^\\s*[\'"](.+?)[\'"]\\s*=>\\s*[\'"](.+)[\'"]/',$l,$match))
  624. $xl[stripslashes($match[1])] = stripslashes($match[2]);
  625. if (isset($xl)) {
  626. if (@$xl['xlpage-i18n']) {
  627. $i18n = preg_replace('/[^-\\w]/','',$xl['xlpage-i18n']);
  628. include_once("$FarmD/scripts/xlpage-$i18n.php");
  629. }
  630. if (@$xl['Locale']) setlocale(LC_ALL,$xl['Locale']);
  631. if (@$xl['TimeFmt']) $TimeFmt=$xl['TimeFmt'];
  632. array_unshift($XLLangs,$lang);
  633. XLSDV($lang,$xl);
  634. }
  635. }
  636. ## CmpPageAttr is used with uksort to order a page's elements with
  637. ## the latest items first. This can make some operations more efficient.
  638. function CmpPageAttr($a, $b) {
  639. @list($x, $agmt) = explode(':', $a);
  640. @list($x, $bgmt) = explode(':', $b);
  641. if ($agmt != $bgmt)
  642. return ($agmt==0 || $bgmt==0) ? $agmt - $bgmt : $bgmt - $agmt;
  643. return strcmp($a, $b);
  644. }
  645. ## class PageStore holds objects that store pages via the native
  646. ## filesystem.
  647. class PageStore {
  648. var $dirfmt;
  649. var $iswrite;
  650. function PageStore($d='$WorkDir/$FullName', $w=0)
  651. { $this->dirfmt=$d; $this->iswrite=$w; }
  652. function pagefile($pagename) {
  653. global $FarmD;
  654. $dfmt = $this->dirfmt;
  655. if ($pagename > '') {
  656. $pagename = str_replace('/', '.', $pagename);
  657. if ($dfmt == 'wiki.d/{$FullName}') # optimizations for
  658. return "wiki.d/$pagename"; # standard locations
  659. if ($dfmt == '$FarmD/wikilib.d/{$FullName}') #
  660. return "$FarmD/wikilib.d/$pagename"; #
  661. if ($dfmt == 'wiki.d/{$Group}/{$FullName}')
  662. return preg_replace('/([^.]+).*/', 'wiki.d/$1/$0', $pagename);
  663. }
  664. return FmtPageName($dfmt, $pagename);
  665. }
  666. function read($pagename, $since=0) {
  667. $newline = '';
  668. $urlencoded = false;
  669. $pagefile = $this->pagefile($pagename);
  670. if ($pagefile && ($fp=@fopen($pagefile, "r"))) {
  671. while (!feof($fp)) {
  672. $line = fgets($fp, 4096);
  673. while (substr($line, -1, 1) != "\n" && !feof($fp))
  674. { $line .= fgets($fp, 4096); }
  675. $line = rtrim($line);
  676. if ($urlencoded) $line = urldecode(str_replace('+', '%2b', $line));
  677. @list($k,$v) = explode('=', $line, 2);
  678. if (!$k) continue;
  679. if ($k == 'version') {
  680. $ordered = (strpos($v, 'ordered=1') !== false);
  681. $urlencoded = (strpos($v, 'urlencoded=1') !== false);
  682. if (strpos($v, 'pmwiki-0.')!==false) $newline="\262";
  683. }
  684. if ($k == 'newline') { $newline = $v; continue; }
  685. if ($since > 0 && preg_match('/:(\\d+)/', $k, $m) && $m[1] < $since) {
  686. if ($ordered) break;
  687. continue;
  688. }
  689. if ($newline) $v = str_replace($newline, "\n", $v);
  690. $page[$k] = $v;
  691. }
  692. fclose($fp);
  693. }
  694. return @$page;
  695. }
  696. function write($pagename,$page) {
  697. global $Now, $Version;
  698. $page['name'] = $pagename;
  699. $page['time'] = $Now;
  700. $page['host'] = $_SERVER['REMOTE_ADDR'];
  701. $page['agent'] = @$_SERVER['HTTP_USER_AGENT'];
  702. $page['rev'] = @$page['rev']+1;
  703. unset($page['version']); unset($page['newline']);
  704. uksort($page, 'CmpPageAttr');
  705. $s = false;
  706. $pagefile = $this->pagefile($pagename);
  707. $dir = dirname($pagefile); mkdirp($dir);
  708. if (!file_exists("$dir/.htaccess") && $fp = @fopen("$dir/.htaccess", "w"))
  709. { fwrite($fp, "Order Deny,Allow\nDeny from all\n"); fclose($fp); }
  710. if ($pagefile && ($fp=fopen("$pagefile,new","w"))) {
  711. $r0 = array('%', "\n", '<');
  712. $r1 = array('%25', '%0a', '%3c');
  713. $x = "version=$Version ordered=1 urlencoded=1\n";
  714. $s = true && fputs($fp, $x); $sz = strlen($x);
  715. foreach($page as $k=>$v)
  716. if ($k > '' && $k{0} != '=') {
  717. $x = str_replace($r0, $r1, "$k=$v") . "\n";
  718. $s = $s && fputs($fp, $x); $sz += strlen($x);
  719. }
  720. $s = fclose($fp) && $s;
  721. $s = $s && (filesize("$pagefile,new") > $sz * 0.95);
  722. if (file_exists($pagefile)) $s = $s && unlink($pagefile);
  723. $s = $s && rename("$pagefile,new", $pagefile);
  724. }
  725. $s && fixperms($pagefile);
  726. if (!$s)
  727. Abort("Cannot write page to $pagename ($pagefile)...changes not saved");
  728. PCache($pagename, $page);
  729. }
  730. function exists($pagename) {
  731. if (!$pagename) return false;
  732. $pagefile = $this->pagefile($pagename);
  733. return ($pagefile && file_exists($pagefile));
  734. }
  735. function delete($pagename) {
  736. global $Now;
  737. $pagefile = $this->pagefile($pagename);
  738. @rename($pagefile,"$pagefile,del-$Now");
  739. }
  740. function ls($pats=NULL) {
  741. global $GroupPattern, $NamePattern;
  742. StopWatch("PageStore::ls begin {$this->dir}");
  743. $pats=(array)$pats;
  744. array_push($pats, "/^$GroupPattern\.$NamePattern$/");
  745. $dir = $this->pagefile('$Group.$Name');
  746. $dirlist = array(preg_replace('!/*[^/]*\\$.*$!','',$dir));
  747. $out = array();
  748. while (count($dirlist)>0) {
  749. $dir = array_shift($dirlist);
  750. $dfp = @opendir($dir); if (!$dfp) { continue; }
  751. $o = array();
  752. while ( ($pagefile = readdir($dfp)) !== false) {
  753. if ($pagefile{0} == '.') continue;
  754. if (is_dir("$dir/$pagefile"))
  755. { array_push($dirlist,"$dir/$pagefile"); continue; }
  756. $o[] = $pagefile;
  757. }
  758. closedir($dfp);
  759. StopWatch("PageStore::ls merge {$this->dir}");
  760. $out = array_merge($out, MatchPageNames($o, $pats));
  761. }
  762. StopWatch("PageStore::ls end {$this->dir}");
  763. return $out;
  764. }
  765. }
  766. function ReadPage($pagename, $since=0) {
  767. # read a page from the appropriate directories given by $WikiReadDirsFmt.
  768. global $WikiLibDirs,$Now;
  769. foreach ($WikiLibDirs as $dir) {
  770. $page = $dir->read($pagename, $since);
  771. if ($page) break;
  772. }
  773. if (@!$page) $page['ctime'] = $Now;
  774. if (@!$page['time']) $page['time'] = $Now;
  775. return $page;
  776. }
  777. function WritePage($pagename,$page) {
  778. global $WikiLibDirs,$WikiDir,$LastModFile;
  779. $WikiDir->iswrite = 1;
  780. for($i=0; $i<count($WikiLibDirs); $i++) {
  781. $wd = &$WikiLibDirs[$i];
  782. if ($wd->iswrite && $wd->exists($pagename)) break;
  783. }
  784. if ($i >= count($WikiLibDirs)) $wd = &$WikiDir;
  785. $wd->write($pagename,$page);
  786. if ($LastModFile && !@touch($LastModFile))
  787. { unlink($LastModFile); touch($LastModFile); fixperms($LastModFile); }
  788. }
  789. function PageExists($pagename) {
  790. ## note: $PageExistsCache might change or disappear someday
  791. global $WikiLibDirs, $PageExistsCache;
  792. if (!isset($PageExistsCache[$pagename])) {
  793. foreach((array)$WikiLibDirs as $dir)
  794. if ($PageExistsCache[$pagename] = $dir->exists($pagename)) break;
  795. }
  796. return $PageExistsCache[$pagename];
  797. }
  798. function ListPages($pat=NULL) {
  799. global $WikiLibDirs;
  800. foreach((array)$WikiLibDirs as $dir)
  801. $out = array_unique(array_merge($dir->ls($pat),(array)@$out));
  802. return $out;
  803. }
  804. function RetrieveAuthPage($pagename, $level, $authprompt=true, $since=0) {
  805. global $AuthFunction;
  806. SDV($AuthFunction,'PmWikiAuth');
  807. if (!function_exists($AuthFunction)) return ReadPage($pagename, $since);
  808. return $AuthFunction($pagename, $level, $authprompt, $since);
  809. }
  810. function Abort($msg) {
  811. # exit pmwiki with an abort message
  812. echo "<h3>PmWiki can't process your request</h3>
  813. <p>$msg</p><p>We are sorry for any inconvenience.</p>";
  814. exit;
  815. }
  816. function Redirect($pagename,$urlfmt='$PageUrl') {
  817. # redirect the browser to $pagename
  818. global $EnableRedirect,$RedirectDelay;
  819. SDV($RedirectDelay,0);
  820. clearstatcache();
  821. #if (!PageExists($pagename)) $pagename=$DefaultPage;
  822. $pageurl = FmtPageName($urlfmt,$pagename);
  823. if (IsEnabled($EnableRedirect,1) &&
  824. (!isset($_REQUEST['redirect']) || $_REQUEST['redirect'])) {
  825. header("Location: $pageurl");
  826. header("Content-type: text/html");
  827. echo "<html><head>
  828. <meta http-equiv='Refresh' Content='$RedirectDelay; URL=$pageurl' />
  829. <title>Redirect</title></head><body></body></html>";
  830. } else echo "<a href='$pageurl'>Redirect to $pageurl</a>";
  831. exit;
  832. }
  833. function PrintFmt($pagename,$fmt) {
  834. global $HTTPHeaders,$FmtV;
  835. if (is_array($fmt))
  836. { foreach($fmt as $f) PrintFmt($pagename,$f); return; }
  837. if ($fmt == 'headers:') {
  838. foreach($HTTPHeaders as $h) (@$sent++) ? @header($h) : header($h);
  839. return;
  840. }
  841. $x = FmtPageName($fmt,$pagename);
  842. if (strncmp($fmt, 'function:', 9) == 0 &&
  843. preg_match('/^function:(\S+)\s*(.*)$/s', $x, $match) &&
  844. function_exists($match[1]))
  845. { $match[1]($pagename,$match[2]); return; }
  846. if (strncmp($fmt, 'file:', 5) == 0 && preg_match("/^file:(.+)/s",$x,$match)) {
  847. $filelist = preg_split('/[\\s]+/',$match[1],-1,PREG_SPLIT_NO_EMPTY);
  848. foreach($filelist as $f) {
  849. if (file_exists($f)) { include($f); return; }
  850. }
  851. return;
  852. }
  853. if (preg_match("/^markup:(.*)$/",$x,$match))
  854. { print MarkupToHTML($pagename,$match[1]); return; }
  855. if (preg_match('/^wiki:(.+)$/', $x, $match))
  856. { PrintWikiPage($pagename, $match[1], 'read'); return; }
  857. if (preg_match('/^page:(.+)$/', $x, $match))
  858. { PrintWikiPage($pagename, $match[1], ''); return; }
  859. echo $x;
  860. }
  861. function PrintWikiPage($pagename, $wikilist=NULL, $auth='read') {
  862. if (is_null($wikilist)) $wikilist=$pagename;
  863. $pagelist = preg_split('/\s+/',$wikilist,-1,PREG_SPLIT_NO_EMPTY);
  864. foreach($pagelist as $p) {
  865. if (PageExists($p)) {
  866. $page = ($auth) ? RetrieveAuthPage($p, $auth, false, READPAGE_CURRENT)
  867. : ReadPage($p, READPAGE_CURRENT);
  868. if ($page['text'])
  869. echo MarkupToHTML($pagename,$page['text']);
  870. return;
  871. }
  872. }
  873. }
  874. function Keep($x, $pool=NULL) {
  875. # Keep preserves a string from being processed by wiki markups
  876. global $BlockPattern, $KeepToken, $KPV, $KPCount;
  877. $x = preg_replace("/$KeepToken(\\d.*?)$KeepToken/e", "\$KPV['\$1']", $x);
  878. if (is_null($pool) && preg_match("!</?($BlockPattern)\\b!", $x)) $pool = 'B';
  879. $KPCount++; $KPV[$KPCount.$pool]=$x;
  880. return $KeepToken.$KPCount.$pool.$KeepToken;
  881. }
  882. function CondText($pagename,$condspec,$condtext) {
  883. global $Conditions;
  884. if (!preg_match("/^(\\S+)\\s*(!?)\\s*(\\S+)?\\s*(.*?)\\s*$/",
  885. $condspec,$match)) return '';
  886. @list($condstr,$condtype,$not,$condname,$condparm) = $match;
  887. if (isset($Conditions[$condname])) {
  888. $tf = @eval("return (".$Conditions[$condname].");");
  889. if (!$tf xor $not) $condtext='';
  890. }
  891. return $condtext;
  892. }
  893. function IncludeText($pagename, $inclspec) {
  894. global $MaxIncludes, $InclCount;
  895. SDV($MaxIncludes,50);
  896. $npat = '[[:alpha:]][-\\w]*';
  897. if ($InclCount++>=$MaxIncludes) return Keep($inclspec);
  898. $args = array_merge(array('self' => 1), ParseArgs($inclspec));
  899. while (count($args['#'])>0) {
  900. $k = array_shift($args['#']); $v = array_shift($args['#']);
  901. if ($k=='') {
  902. preg_match('/^([^#\\s]*)(.*)$/', $v, $match);
  903. if ($match[1]) { # include a page
  904. if (isset($itext)) continue;
  905. $iname = MakePageName($pagename, $match[1]);
  906. if (!$args['self'] && $iname == $pagename) continue;
  907. if (!PageExists($iname)) continue;
  908. $ipage = RetrieveAuthPage($iname, 'read', false, READPAGE_CURRENT);
  909. $itext = @$ipage['text'];
  910. }
  911. if (preg_match("/^#($npat)?(\\.\\.)?(#($npat)?)?$/", $match[2], $m)) {
  912. @list($x, $aa, $dots, $b, $bb) = $m;
  913. if (!$dots && !$b) $bb = $npat;
  914. if ($aa)
  915. $itext=preg_replace("/^.*?\n([^\n]*\\[\\[#$aa\\]\\])/s",
  916. '$1', $itext, 1);
  917. if ($bb)
  918. $itext=preg_replace("/(\n)[^\n]*\\[\\[#$bb\\]\\].*$/s",
  919. '$1', $itext, 1);
  920. }
  921. continue;
  922. }
  923. if (in_array($k, array('line', 'lines', 'para', 'paras'))) {
  924. preg_match('/^(\\d*)(\\.\\.(\\d*))?$/', $v, $match);
  925. @list($x, $a, $dots, $b) = $match;
  926. $upat = ($k{0} == 'p') ? ".*?(\n\\s*\n|$)" : "[^\n]*(?:\n|$)";
  927. if (!$dots) { $b=$a; $a=0; }
  928. if ($a>0) $a--;
  929. $itext=preg_replace("/^(($upat){0,$b}).*$/s",'$1',$itext,1);
  930. $itext=preg_replace("/^($upat){0,$a}/s",'',$itext,1);
  931. continue;
  932. }
  933. }
  934. return PVS(htmlspecialchars(@$itext, ENT_NOQUOTES));
  935. }
  936. function RedirectMarkup($pagename, $opt) {
  937. $k = Keep("(:redirect $opt:)");
  938. global $MarkupFrame;
  939. if (!@$MarkupFrame[0]['redirect']) return $k;
  940. $opt = ParseArgs($opt);
  941. $to = @$opt['to']; if (!$to) $to = @$opt[''][0];
  942. if (!$to) return $k;
  943. if (preg_match('/^([^#]+)(#[A-Za-z][-\\w]*)$/', $to, $match))
  944. { $to = $match[1]; $anchor = $match[2]; }
  945. $to = MakePageName($pagename, $to);
  946. if (!PageExists($to)) return $k;
  947. if ($to == $pagename) return '';
  948. if (@$opt['from']
  949. && !MatchPageNames($pagename, FixGlob($opt['from'], '$1*.$2')))
  950. return '';
  951. if (preg_match('/^30[1237]$/', @$opt['status']))
  952. header("HTTP/1.1 {$opt['status']}");
  953. Redirect($to, "{\$PageUrl}?from=$pagename$anchor");
  954. exit();
  955. }
  956. function Block($b) {
  957. global $BlockMarkups,$HTMLVSpace,$HTMLPNewline,$MarkupFrame;
  958. $mf = &$MarkupFrame[0]; $cs = &$mf['cs']; $vspaces = &$mf['vs'];
  959. $out = '';
  960. if ($b == 'vspace') {
  961. $vspaces .= "\n";
  962. while (count($cs)>0 && @end($cs)!='pre' && @$BlockMarkups[@end($cs)][3]==0)
  963. { $c = array_pop($cs); $out .= $BlockMarkups[$c][2]; }
  964. return $out;
  965. }
  966. @list($code, $depth, $icol) = explode(',', $b);
  967. if (!$code) $depth = 1;
  968. if ($depth == 0) $depth = strlen($depth);
  969. if ($icol == 0) $icol = strlen($icol);
  970. if ($depth > 0) $depth += @$mf['idep'];
  971. if ($icol > 0) $mf['is'][$depth] = $icol + @$mf['icol'];
  972. @$mf['idep'] = @$mf['icol'] = 0;
  973. while (count($cs)>$depth)
  974. { $c = array_pop($cs); $out .= $BlockMarkups[$c][2]; }
  975. if (!$code) {
  976. if (@end($cs) == 'p') { $out .= $HTMLPNewline; $code = 'p'; }
  977. else if ($depth < 2) { $code = 'p'; $mf['is'][$depth] = 0; }
  978. else { $out .= $HTMLPNewline; $code = 'block'; }
  979. }
  980. if ($depth>0 && $depth==count($cs) && $cs[$depth-1]!=$code)
  981. { $c = array_pop($cs); $out .= $BlockMarkups[$c][2]; }
  982. while (count($cs)>0 && @end($cs)!=$code &&
  983. @$BlockMarkups[@end($cs)][3]==0)
  984. { $c = array_pop($cs); $out .= $BlockMarkups[$c][2]; }
  985. if ($vspaces) {
  986. $out .= (@end($cs) == 'pre') ? $vspaces : $HTMLVSpace;
  987. $vspaces='';
  988. }
  989. if ($depth==0) { return $out; }
  990. if ($depth==count($cs)) { return $out.$BlockMarkups[$code][1]; }
  991. while (count($cs)<$depth-1) {
  992. array_push($cs, 'dl'); $mf['is'][count($cs)] = 0;
  993. $out .= $BlockMarkups['dl'][0].'<dd>';
  994. }
  995. if (count($cs)<$depth) {
  996. array_push($cs,$code);
  997. $out .= $BlockMarkups[$code][0];
  998. }
  999. return $out;
  1000. }
  1001. function MarkupClose($key = '') {
  1002. global $MarkupFrame;
  1003. $cf = & $MarkupFrame[0]['closeall'];
  1004. $out = '';
  1005. if ($key == '' || isset($cf[$key])) {
  1006. $k = array_keys((array)$cf);
  1007. while ($k) {
  1008. $x = array_pop($k); $out .= $cf[$x]; unset($cf[$x]);
  1009. if ($x == $key) break;
  1010. }
  1011. }
  1012. return $out;
  1013. }
  1014. function FormatTableRow($x) {
  1015. global $Block, $TableCellAttrFmt, $MarkupFrame, $TableRowAttrFmt,
  1016. $TableRowIndexMax, $FmtV;
  1017. static $rowcount;
  1018. $x = preg_replace('/\\|\\|\\s*$/','',$x);
  1019. $td = explode('||',$x); $y='';
  1020. for($i=0;$i<count($td);$i++) {
  1021. if ($td[$i]=='') continue;
  1022. $FmtV['$TableCellCount'] = $i;
  1023. $attr = FmtPageName($TableCellAttrFmt, '');
  1024. $td[$i] = preg_replace('/^(!?)\\s+$/', '$1&nbsp;', $td[$i]);
  1025. if (preg_match('/^!(.*?)!$/',$td[$i],$match))
  1026. { $td[$i]=$match[1]; $t='caption'; $attr=''; }
  1027. elseif (preg_match('/^!(.*)$/',$td[$i],$match))
  1028. { $td[$i]=$match[1]; $t='th'; }
  1029. else $t='td';
  1030. if (preg_match('/^\\s.*\\s$/',$td[$i])) { $attr .= " align='center'"; }
  1031. elseif (preg_match('/^\\s/',$td[$i])) { $attr .= " align='right'"; }
  1032. elseif (preg_match('/\\s$/',$td[$i])) { $attr .= " align='left'"; }
  1033. for ($colspan=1;$i+$colspan<count($td);$colspan++)
  1034. if ($td[$colspan+$i]!='') break;
  1035. if ($colspan>1) { $attr .= " colspan='$colspan'"; }
  1036. $y .= "<$t $attr>".trim($td[$i])."</$t>";
  1037. }
  1038. if ($t=='caption') return "<:table,1>$y";
  1039. if (@$MarkupFrame[0]['cs'][0] != 'table') $rowcount = 0; else $rowcount++;
  1040. $FmtV['$TableRowCount'] = $rowcount + 1;
  1041. $FmtV['$TableRowIndex'] = ($rowcount % $TableRowIndexMax) + 1;
  1042. $trattr = FmtPageName($TableRowAttrFmt, '');
  1043. return "<:table,1><tr $trattr>$y</tr>";
  1044. }
  1045. function WikiLink($pagename, $word) {
  1046. global $LinkWikiWords, $SpaceWikiWords, $AsSpacedFunction,
  1047. $MarkupFrame, $WikiWordCountMax;
  1048. if (!$LinkWikiWords) return $word;
  1049. $text = ($SpaceWikiWords) ? $AsSpacedFunction($word) : $word;
  1050. $text = preg_replace('!.*/!', '', $text);
  1051. if (!isset($MarkupFrame[0]['wwcount'][$word]))
  1052. $MarkupFrame[0]['wwcount'][$word] = $WikiWordCountMax;
  1053. if ($MarkupFrame[0]['wwcount'][$word]-- < 1) return $text;
  1054. return MakeLink($pagename, $word, $text);
  1055. }
  1056. function LinkIMap($pagename,$imap,$path,$title,$txt,$fmt=NULL) {
  1057. global $FmtV, $IMap, $IMapLinkFmt, $UrlLinkFmt;
  1058. $FmtV['$LinkUrl'] = PUE(str_replace('$1',$path,$IMap[$imap]));
  1059. $FmtV['$LinkText'] = $txt;
  1060. $FmtV['$LinkAlt'] = str_replace(array('"',"'"),array('&#34;','&#39;'),$title);
  1061. if (!$fmt)
  1062. $fmt = (isset($IMapLinkFmt[$imap])) ? $IMapLinkFmt[$imap] : $UrlLinkFmt;
  1063. return str_replace(array_keys($FmtV),array_values($FmtV),$fmt);
  1064. }
  1065. function LinkPage($pagename,$imap,$path,$title,$txt,$fmt=NULL) {
  1066. global $QueryFragPattern,$LinkPageExistsFmt,$LinkPageSelfFmt,
  1067. $LinkPageCreateSpaceFmt,$LinkPageCreateFmt,$FmtV,$LinkTargets;
  1068. if (!$fmt && $path{0} == '#') {
  1069. $path = preg_replace("/[^-.:\\w]/", '', $path);
  1070. return ($path) ? "<a href='#$path'>$txt</a>" : '';
  1071. }
  1072. if (!preg_match("/^\\s*([^#?]+)($QueryFragPattern)?$/",$path,$match))
  1073. return '';
  1074. $tgtname = MakePageName($pagename, $match[1]);
  1075. if (!$tgtname) return '';
  1076. $qf = @$match[2];
  1077. @$LinkTargets[$tgtname]++;
  1078. if (!$fmt) {
  1079. if (!PageExists($tgtname) && !preg_match('/[&?]action=/', $qf))
  1080. $fmt = preg_match('/\\s/', $txt)
  1081. ? $LinkPageCreateSpaceFmt : $LinkPageCreateFmt;
  1082. else
  1083. $fmt = ($tgtname == $pagename && $qf == '')
  1084. ? $LinkPageSelfFmt : $LinkPageExistsFmt;
  1085. }
  1086. $fmt = str_replace(array('$LinkUrl', '$LinkText'),
  1087. array(PageVar($tgtname, '$PageUrl').PUE($qf), $txt), $fmt);
  1088. return FmtPageName($fmt,$tgtname);
  1089. }
  1090. function MakeLink($pagename,$tgt,$txt=NULL,$suffix=NULL,$fmt=NULL) {
  1091. global $LinkPattern,$LinkFunctions,$UrlExcludeChars,$ImgExtPattern,$ImgTagFmt;
  1092. $t = preg_replace('/[()]/','',trim($tgt));
  1093. $t = preg_replace('/<[^>]*>/','',$t);
  1094. preg_match("/^($LinkPattern)?(.+?)(\"(.*)\")?$/",$t,$m);
  1095. if (!$m[1]) $m[1]='<:page>';
  1096. if (preg_match("/(($LinkPattern)([^$UrlExcludeChars]+$ImgExtPattern))(\"(.*)\")?$/",$txt,$tm))
  1097. $txt = $LinkFunctions[$tm[2]]($pagename,$tm[2],$tm[3],@$tm[5],
  1098. $tm[1],$ImgTagFmt);
  1099. else {
  1100. if (is_null($txt)) {
  1101. $txt = preg_replace('/\\([^)]*\\)/','',$tgt);
  1102. if ($m[1]=='<:page>') {
  1103. $txt = preg_replace('!/\\s*$!', '', $txt);
  1104. $txt = preg_replace('!^.*[^<]/!', '', $txt);
  1105. }
  1106. }
  1107. $txt .= $suffix;
  1108. }
  1109. $out = $LinkFunctions[$m[1]]($pagename,$m[1],$m[2],@$m[4],$txt,$fmt);
  1110. return $out;
  1111. }
  1112. function Markup($id,$cmd,$pat=NULL,$rep=NULL) {
  1113. global $MarkupTable,$MarkupRules;
  1114. unset($MarkupRules);
  1115. if (preg_match('/^([<>])?(.+)$/',$cmd,$m)) {
  1116. $MarkupTable[$id]['cmd']=$cmd;
  1117. $MarkupTable[$m[2]]['dep'][$id] = $m[1];
  1118. if (!$m[1]) $m[1]='=';
  1119. if (@$MarkupTable[$m[2]]['seq']) {
  1120. $MarkupTable[$id]['seq'] = $MarkupTable[$m[2]]['seq'].$m[1];
  1121. foreach((array)@$MarkupTable[$id]['dep'] as $i=>$m)
  1122. Markup($i,"$m$id");
  1123. unset($MarkupTable[$id]['dep']);
  1124. }
  1125. }
  1126. if ($pat && !isset($MarkupTable[$id]['pat'])) {
  1127. $MarkupTable[$id]['pat']=$pat;
  1128. $MarkupTable[$id]['rep']=$rep;
  1129. }
  1130. }
  1131. function DisableMarkup() {
  1132. global $MarkupTable;
  1133. $idlist = func_get_args();
  1134. unset($MarkupRules);
  1135. while (count($idlist)>0) {
  1136. $id = array_shift($idlist);
  1137. if (is_array($id)) { $idlist = array_merge($idlist, $id); continue; }
  1138. $MarkupTable[$id] = array('cmd' => 'none', pat=>'');
  1139. }
  1140. }
  1141. function mpcmp($a,$b) { return @strcmp($a['seq'].'=',$b['seq'].'='); }
  1142. function BuildMarkupRules() {
  1143. global $MarkupTable,$MarkupRules,$LinkPattern;
  1144. if (!$MarkupRules) {
  1145. uasort($MarkupTable,'mpcmp');
  1146. foreach($MarkupTable as $id=>$m)
  1147. if (@$m['pat'])
  1148. $MarkupRules[str_replace('\\L',$LinkPattern,$m['pat'])]=$m['rep'];
  1149. }
  1150. return $MarkupRules;
  1151. }
  1152. function MarkupToHTML($pagename, $text, $opt = NULL) {
  1153. # convert wiki markup text to HTML output
  1154. global $MarkupRules, $MarkupFrame, $MarkupFrameBase, $WikiWordCount,
  1155. $K0, $K1, $RedoMarkupLine;
  1156. StopWatch('MarkupToHTML begin');
  1157. array_unshift($MarkupFrame, array_merge($MarkupFrameBase, (array)$opt));
  1158. $MarkupFrame[0]['wwcount'] = $WikiWordCount;
  1159. $markrules = BuildMarkupRules();
  1160. foreach((array)$text as $l)
  1161. $lines[] = $MarkupFrame[0]['escape']
  1162. ? PVS(htmlspecialchars($l, ENT_NOQUOTES)) : $l;
  1163. $lines[] = '(:closeall:)';
  1164. $out = '';
  1165. while (count($lines)>0) {
  1166. $x = array_shift($lines);
  1167. $RedoMarkupLine=0;
  1168. foreach($markrules as $p=>$r) {
  1169. if ($p{0} == '/') $x=preg_replace($p,$r,$x);
  1170. elseif (strstr($x,$p)!==false) $x=eval($r);
  1171. if (isset($php_errormsg)) { echo "pat=$p"; unset($php_errormsg); }
  1172. if ($RedoMarkupLine) { $lines=array_merge((array)$x,$lines); continue 2; }
  1173. }
  1174. if ($x>'') $out .= "$x\n";
  1175. }
  1176. foreach((array)(@$MarkupFrame[0]['posteval']) as $v) eval($v);
  1177. array_shift($MarkupFrame);
  1178. StopWatch('MarkupToHTML end');
  1179. return $out;
  1180. }
  1181. function HandleBrowse($pagename, $auth = 'read') {
  1182. # handle display of a page
  1183. global $DefaultPageTextFmt, $PageNotFoundHeaderFmt, $HTTPHeaders,
  1184. $EnableHTMLCache, $NoHTMLCache, $PageCacheFile, $LastModTime, $IsHTMLCached,
  1185. $FmtV, $HandleBrowseFmt, $PageStartFmt, $PageEndFmt, $PageRedirectFmt;
  1186. $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT);
  1187. if (!$page) Abort('?cannot read $pagename');
  1188. PCache($pagename,$page);
  1189. if (PageExists($pagename)) $text = @$page['text'];
  1190. else {
  1191. SDV($DefaultPageTextFmt,'(:include $[{$SiteGroup}.PageNotFound]:)');
  1192. $text = FmtPageName($DefaultPageTextFmt, $pagename);
  1193. SDV($PageNotFoundHeaderFmt, 'HTTP/1.1 404 Not Found');
  1194. SDV($HTTPHeaders['status'], $PageNotFoundHeaderFmt);
  1195. }
  1196. $opt = array();
  1197. SDV($PageRedirectFmt,"<p><i>($[redirected from] <a rel='nofollow'
  1198. href='{\$PageUrl}?action=edit'>{\$FullName}</a>)</i></p>\$HTMLVSpace\n");
  1199. if (@!$_GET['from']) { $opt['redirect'] = 1; $PageRedirectFmt = ''; }
  1200. else $PageRedirectFmt = FmtPageName($PageRedirectFmt, $_GET['from']);
  1201. if (@$EnableHTMLCache && !$NoHTMLCache && $PageCacheFile &&
  1202. @filemtime($PageCacheFile) > $LastModTime) {
  1203. list($ctext) = unserialize(file_get_contents($PageCacheFile));
  1204. $FmtV['$PageText'] = "<!--cached-->$ctext";
  1205. $IsHTMLCached = 1;
  1206. } else {
  1207. $IsHTMLCached = 0;
  1208. $text = '(:groupheader:)'.@$text.'(:groupfooter:)';
  1209. $t1 = time();
  1210. $FmtV['$PageText'] = MarkupToHTML($pagename, $text, $opt);
  1211. if (@$EnableHTMLCache > 0 && !$NoHTMLCache && $PageCacheFile
  1212. && (time() - $t1 + 1) >= $EnableHTMLCache) {
  1213. $fp = @fopen("$PageCacheFile,new", "x");
  1214. if ($fp) {
  1215. fwrite($fp, serialize(array($FmtV['$PageText']))); fclose($fp);
  1216. rename("$PageCacheFile,new", $PageCacheFile);
  1217. }
  1218. }
  1219. }
  1220. SDV($HandleBrowseFmt,array(&$PageStartFmt, &$PageRedirectFmt, '$PageText',
  1221. &$PageEndFmt));
  1222. PrintFmt($pagename,$HandleBrowseFmt);
  1223. }
  1224. # EditTemplate allows a site administrator to pre-populate new pages
  1225. # with the contents of another page.
  1226. function EditTemplate($pagename, &$page, &$new) {
  1227. global $EditTemplatesFmt;
  1228. if (@$new['text'] > '') return;
  1229. if (@$_REQUEST['template'] && PageExists($_REQUEST['template'])) {
  1230. $p = RetrieveAuthPage($_REQUEST['template'], 'read', false,
  1231. READPAGE_CURRENT);
  1232. if ($p['text'] > '') $new['text'] = $p['text'];
  1233. return;
  1234. }
  1235. foreach((array)$EditTemplatesFmt as $t) {
  1236. $p = RetrieveAuthPage(FmtPageName($t,$pagename), 'read', false,
  1237. READPAGE_CURRENT);
  1238. if (@$p['text'] > '') { $new['text'] = $p['text']; return; }
  1239. }
  1240. }
  1241. # RestorePage handles returning to the version of text as of
  1242. # the version given by $restore or $_REQUEST['restore'].
  1243. function RestorePage($pagename,&$page,&$new,$restore=NULL) {
  1244. if (is_null($restore)) $restore=@$_REQUEST['restore'];
  1245. if (!$restore) return;
  1246. $t = $page['text'];
  1247. $nl = (substr($t,-1)=="\n");
  1248. $t = explode("\n",$t);
  1249. if ($nl) array_pop($t);
  1250. krsort($page); reset($page);
  1251. foreach($page as $k=>$v) {
  1252. if ($k<$restore) break;
  1253. if (strncmp($k, 'diff:', 5) != 0) continue;
  1254. foreach(explode("\n",$v) as $x) {
  1255. if (preg_match('/^(\\d+)(,(\\d+))?([adc])(\\d+)/',$x,$match)) {
  1256. $a1 = $a2 = $match[1];
  1257. if ($match[3]) $a2=$match[3];
  1258. $b1 = $match[5];
  1259. if ($match[4]=='d') array_splice($t,$b1,$a2-$a1+1);
  1260. if ($match[4]=='c') array_splice($t,$b1-1,$a2-$a1+1);
  1261. continue;
  1262. }
  1263. if (strncmp($x,'< ',2) == 0) { $nlflag=true; continue; }
  1264. if (preg_match('/^> (.*)$/',$x,$match)) {
  1265. $nlflag=false;
  1266. array_splice($t,$b1-1,0,$match[1]); $b1++;
  1267. }
  1268. if ($x=='\\ No newline at end of file') $nl=$nlflag;
  1269. }
  1270. }
  1271. if ($nl) $t[]='';
  1272. $new['text']=implode("\n",$t);
  1273. return $new['text'];
  1274. }
  1275. ## ReplaceOnSave performs any text replacements (held in $ROSPatterns)
  1276. ## on the new text prior to saving the page.
  1277. function ReplaceOnSave($pagename,&$page,&$new) {
  1278. global $EnablePost, $ROSPatterns;
  1279. if (!$EnablePost) return;
  1280. foreach((array)$ROSPatterns as $pat=>$repfmt)
  1281. $new['text'] =
  1282. preg_replace($pat,FmtPageName($repfmt,$pagename),$new['text']);
  1283. }
  1284. function SaveAttributes($pagename,&$page,&$new) {
  1285. global $EnablePost, $LinkTargets, $SaveAttrPatterns, $PCache,
  1286. $SaveProperties;
  1287. if (!$EnablePost) return;
  1288. $text = preg_replace(array_keys($SaveAttrPatterns),
  1289. array_values($SaveAttrPatterns), $new['text']);
  1290. $html = MarkupToHTML($pagename,$text);
  1291. $new['targets'] = implode(',',array_keys((array)$LinkTargets));
  1292. $p = & $PCache[$pagename];
  1293. foreach((array)$SaveProperties as $k) {
  1294. if (@$p["=p_$k"]) $new[$k] = $p["=p_$k"];
  1295. else unset($new[$k]);
  1296. }
  1297. unset($new['excerpt']);
  1298. }
  1299. function PostPage($pagename, &$page, &$new) {
  1300. global $DiffKeepDays, $DiffFunction, $DeleteKeyPattern, $EnablePost,
  1301. $Now, $Author, $WikiDir, $IsPagePosted;
  1302. SDV($DiffKeepDays,3650);
  1303. SDV($DeleteKeyPattern,"^\\s*delete\\s*$");
  1304. $IsPagePosted = false;
  1305. if ($EnablePost) {
  1306. $new["author"]=@$Author;
  1307. $new["author:$Now"] = @$Author;
  1308. $new["host:$Now"] = $_SERVER['REMOTE_ADDR'];
  1309. $diffclass = preg_replace('/\\W/','',@$_POST['diffclass']);
  1310. if ($page["time"]>0 && function_exists(@$DiffFunction))
  1311. $new["diff:$Now:{$page['time']}:$diffclass"] =
  1312. $DiffFunction($new['text'],@$page['text']);
  1313. $keepgmt = $Now-$DiffKeepDays * 86400;
  1314. $keys = array_keys($new);
  1315. foreach($keys as $k)
  1316. if (preg_match("/^\\w+:(\\d+)/",$k,$match) && $match[1]<$keepgmt)
  1317. unset($new[$k]);
  1318. if (preg_match("/$DeleteKeyPattern/",$new['text']))
  1319. $WikiDir->delete($pagename);
  1320. else WritePage($pagename,$new);
  1321. $IsPagePosted = true;
  1322. }
  1323. }
  1324. function PostRecentChanges($pagename,&$page,&$new) {
  1325. global $IsPagePosted, $RecentChangesFmt, $RCDelimPattern, $RCLinesMax;
  1326. if (!$IsPagePosted) return;
  1327. foreach($RecentChangesFmt as $rcfmt=>$pgfmt) {
  1328. $rcname = FmtPageName($rcfmt,$pagename); if (!$rcname) continue;
  1329. $pgtext = FmtPageName($pgfmt,$pagename); if (!$pgtext) continue;
  1330. if (@$seen[$rcname]++) continue;
  1331. $rcpage = ReadPage($rcname);
  1332. $rcelim = preg_quote(preg_replace("/$RCDelimPattern.*$/",' ',$pgtext),'/');
  1333. $rcpage['text'] = preg_replace("/[^\n]*$rcelim.*\n/","",@$rcpage['text']);
  1334. if (!preg_match("/$RCDelimPattern/",$rcpage['text']))
  1335. $rcpage['text'] .= "$pgtext\n";
  1336. else
  1337. $rcpage['text'] = preg_replace("/([^\n]*$RCDelimPattern.*\n)/",
  1338. "$pgtext\n$1", $rcpage['text'], 1);
  1339. if (@$RCLinesMax > 0)
  1340. $rcpage['text'] = implode("\n", array_slice(
  1341. explode("\n", $rcpage['text'], $RCLinesMax + 1), 0, $RCLinesMax));
  1342. WritePage($rcname, $rcpage);
  1343. }
  1344. }
  1345. function PreviewPage($pagename,&$page,&$new) {
  1346. global $IsPageSaved, $FmtV;
  1347. if (@$_POST['preview']) {
  1348. $text = '(:groupheader:)'.$new['text'].'(:groupfooter:)';
  1349. $FmtV['$PreviewText'] = MarkupToHTML($pagename,$text);
  1350. }
  1351. }
  1352. function HandleEdit($pagename, $auth = 'edit') {
  1353. global $IsPagePosted, $EditFields, $ChangeSummary, $EditFunctions,
  1354. $EnablePost, $FmtV, $Now, $EditRedirectFmt,
  1355. $PageEditForm, $HandleEditFmt, $PageStartFmt, $PageEditFmt, $PageEndFmt;
  1356. SDV($EditRedirectFmt, '$FullName');
  1357. if (@$_POST['cancel'])
  1358. { Redirect(FmtPageName($EditRedirectFmt, $pagename)); return; }
  1359. Lock(2);
  1360. $IsPagePosted = false;
  1361. $page = RetrieveAuthPage($pagename, $auth, true);
  1362. if (!$page) Abort("?cannot edit $pagename");
  1363. PCache($pagename,$page);
  1364. $new = $page;
  1365. foreach((array)$EditFields as $k)
  1366. if (isset($_POST[$k])) $new[$k]=str_replace("\r",'',stripmagic($_POST[$k]));
  1367. $new['csum'] = $ChangeSummary;
  1368. if ($ChangeSummary) $new["csum:$Now"] = $ChangeSummary;
  1369. $EnablePost &= preg_grep('/^post/', array_keys(@$_POST));
  1370. foreach((array)$EditFunctions as $fn) $fn($pagename,$page,$new);
  1371. Lock(0);
  1372. if ($IsPagePosted && !@$_POST['postedit'])
  1373. { Redirect(FmtPageName($EditRedirectFmt, $pagename)); return; }
  1374. $FmtV['$DiffClassMinor'] =
  1375. (@$_POST['diffclass']=='minor') ? "checked='checked'" : '';
  1376. $FmtV['$EditText'] =
  1377. str_replace('$','&#036;',htmlspecialchars(@$new['text'],ENT_NOQUOTES));
  1378. $FmtV['$EditBaseTime'] = $Now;
  1379. if (@$PageEditForm) {
  1380. $form = ReadPage(FmtPageName($PageEditForm, $pagename), READPAGE_CURRENT);
  1381. $FmtV['$EditForm'] = MarkupToHTML($pagename, $form['text']);
  1382. }
  1383. SDV($PageEditFmt, "<div id='wikiedit'>
  1384. <h2 class='wikiaction'>$[Editing {\$FullName}]</h2>
  1385. <form method='post' rel='nofollow' action='\$PageUrl?action=edit'>
  1386. <input type='hidden' name='action' value='edit' />
  1387. <input type='hidden' name='n' value='\$FullName' />
  1388. <input type='hidden' name='basetime' value='\$EditBaseTime' />
  1389. \$EditMessageFmt
  1390. <textarea id='text' name='text' rows='25' cols='60'
  1391. onkeydown='if (event.keyCode==27) event.returnValue=false;'
  1392. >\$EditText</textarea><br />
  1393. <input type='submit' name='post' value=' $[Save] ' />");
  1394. SDV($HandleEditFmt, array(&$PageStartFmt, &$PageEditFmt, &$PageEndFmt));
  1395. PrintFmt($pagename, $HandleEditFmt);
  1396. }
  1397. function HandleSource($pagename, $auth = 'read') {
  1398. global $HTTPHeaders;
  1399. $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT);
  1400. if (!$page) Abort("?cannot source $pagename");
  1401. foreach ($HTTPHeaders as $h) {
  1402. $h = preg_replace('!^Content-type:\\s+text/html!i',
  1403. 'Content-type: text/plain', $h);
  1404. header($h);
  1405. }
  1406. echo @$page['text'];
  1407. }
  1408. ## PmWikiAuth provides password-protection of pages using PHP sessions.
  1409. ## It is normally called from RetrieveAuthPage. Since RetrieveAuthPage
  1410. ## can be called a lot within a single page execution (i.e., for every
  1411. ## page accessed), we cache the results of site passwords and
  1412. ## GroupAttribute pages to be able to speed up subsequent calls.
  1413. function PmWikiAuth($pagename, $level, $authprompt=true, $since=0) {
  1414. global $DefaultPasswords, $GroupAttributesFmt, $AllowPassword,
  1415. $AuthCascade, $FmtV, $AuthPromptFmt, $PageStartFmt, $PageEndFmt,
  1416. $AuthId, $AuthList, $NoHTMLCache;
  1417. static $acache;
  1418. SDV($GroupAttributesFmt,'$Group/GroupAttributes');
  1419. SDV($AllowPassword,'nopass');
  1420. $page = ReadPage($pagename, $since);
  1421. if (!$page) { return false; }
  1422. if (!isset($acache))
  1423. SessionAuth($pagename, (@$_POST['authpw'])
  1424. ? array('authpw' => array($_POST['authpw'] => 1))
  1425. : '');
  1426. if (@$AuthId) {
  1427. $AuthList["id:$AuthId"] = 1;
  1428. $AuthList["id:-$AuthId"] = -1;
  1429. $AuthList["id:*"] = 1;
  1430. }
  1431. $gn = FmtPageName($GroupAttributesFmt, $pagename);
  1432. if (!isset($acache[$gn])) {
  1433. $gp = ReadPage($gn, READPAGE_CURRENT);
  1434. foreach($DefaultPasswords as $k => $v) {
  1435. $x = array(2, array(), '');
  1436. $acache['@site'][$k] = IsAuthorized($v, 'site', $x);
  1437. $AuthList["@_site_$k"] = $acache['@site'][$k][0] ? 1 : 0;
  1438. $acache[$gn][$k] = IsAuthorized($gp["passwd$k"], 'group',
  1439. $acache['@site'][$k]);
  1440. }
  1441. }
  1442. foreach($DefaultPasswords as $k => $v)
  1443. list($page['=auth'][$k], $page['=passwd'][$k], $page['=pwsource'][$k]) =
  1444. IsAuthorized($page["passwd$k"], 'page', $acache[$gn][$k]);
  1445. foreach($AuthCascade as $k => $t) {
  1446. if ($page['=auth'][$k]+0 == 2) {
  1447. $page['=auth'][$k] = $page['=auth'][$t];
  1448. if ($page['=passwd'][$k] = $page['=passwd'][$t]) # assign
  1449. $page['=pwsource'][$k] = "cascade:$t";
  1450. }
  1451. }
  1452. if (@$page['=auth']['admin'])
  1453. foreach($page['=auth'] as $lv=>$a) @$page['=auth'][$lv] = 3;
  1454. if (@$page['=passwd']['read']) $NoHTMLCache |= 2;
  1455. if ($level=='ALWAYS' || @$page['=auth'][$level]) return $page;
  1456. if (!$authprompt) return false;
  1457. $GLOBALS['AuthNeeded'] = (@$_POST['authpw'])
  1458. ? $page['=pwsource'][$level] . ' ' . $level : '';
  1459. PCache($pagename, $page);
  1460. $postvars = '';
  1461. foreach($_POST as $k=>$v) {
  1462. if ($k == 'authpw' || $k == 'authid') continue;
  1463. $v = str_replace('$', '&#036;',
  1464. htmlspecialchars(stripmagic($v), ENT_COMPAT));
  1465. $postvars .= "<input type='hidden' name='$k' value=\"$v\" />\n";
  1466. }
  1467. $FmtV['$PostVars'] = $postvars;
  1468. SDV($AuthPromptFmt,array(&$PageStartFmt,
  1469. "<p><b>$[Password required]</b></p>
  1470. <form name='authform' action='{$_SERVER['REQUEST_URI']}' method='post'>
  1471. $[Password]: <input tabindex='1' type='password' name='authpw'
  1472. value='' />
  1473. <input type='submit' value='OK' />\$PostVars</form>
  1474. <script language='javascript' type='text/javascript'><!--
  1475. document.authform.authpw.focus() //--></script>", &$PageEndFmt));
  1476. PrintFmt($pagename,$AuthPromptFmt);
  1477. exit;
  1478. }
  1479. function IsAuthorized($chal, $source, &$from) {
  1480. global $AuthList, $AuthPw, $AllowPassword;
  1481. if (!$chal) return $from;
  1482. $auth = 0;
  1483. $passwd = array();
  1484. foreach((array)$chal as $c) {
  1485. $x = '';
  1486. $pwchal = preg_split('/([, ]|\\w+:)/', $c, -1, PREG_SPLIT_DELIM_CAPTURE);
  1487. foreach($pwchal as $pw) {
  1488. if ($pw == ',') continue;
  1489. else if ($pw == ' ') { $x = ''; continue; }
  1490. else if (substr($pw, -1, 1) == ':') { $x = $pw; continue; }
  1491. else if ($pw{0} != '@' && $x > '') $pw = $x . $pw;
  1492. if (!$pw) continue;
  1493. $passwd[] = $pw;
  1494. if ($auth < 0) continue;
  1495. if ($x || $pw{0} == '@') {
  1496. if (@$AuthList[$pw]) $auth = $AuthList[$pw];
  1497. continue;
  1498. }
  1499. if (crypt($AllowPassword, $pw) == $pw) # nopass
  1500. { $auth=1; continue; }
  1501. foreach((array)$AuthPw as $pwresp) # password
  1502. if (crypt($pwresp, $pw) == $pw) { $auth=1; continue; }
  1503. }
  1504. }
  1505. if (!$passwd) return $from;
  1506. if ($auth < 0) $auth = 0;
  1507. return array($auth, $passwd, $source);
  1508. }
  1509. ## SessionAuth works with PmWikiAuth to manage authorizations
  1510. ## as stored in sessions. First, it can be used to set session
  1511. ## variables by calling it with an $auth argument. It then
  1512. ## uses the authid, authpw, and authlist session variables
  1513. ## to set the corresponding values of $AuthId, $AuthPw, and $AuthList
  1514. ## as needed.
  1515. function SessionAuth($pagename, $auth = NULL) {
  1516. global $AuthId, $AuthList, $AuthPw;
  1517. static $called;
  1518. @$called++;
  1519. if (!$auth && ($called > 1 || !@$_REQUEST[session_name()])) return;
  1520. $sid = session_id();
  1521. @session_start();
  1522. foreach((array)$auth as $k => $v)
  1523. if ($k) $_SESSION[$k] = (array)$v + (array)$_SESSION[$k];
  1524. if (!isset($AuthId)) $AuthId = @end($_SESSION['authid']);
  1525. $AuthPw = array_keys((array)@$_SESSION['authpw']);
  1526. $AuthList = array_merge($AuthList, (array)@$_SESSION['authlist']);
  1527. if (!$sid) session_write_close();
  1528. }
  1529. function PrintAttrForm($pagename) {
  1530. global $PageAttributes, $PCache, $FmtV;
  1531. echo FmtPageName("<form action='\$PageUrl' method='post'>
  1532. <input type='hidden' name='action' value='postattr' />
  1533. <input type='hidden' name='n' value='\$FullName' />
  1534. <table>",$pagename);
  1535. $page = $PCache[$pagename];
  1536. foreach($PageAttributes as $attr=>$p) {
  1537. if (!$p) continue;
  1538. $setting = @$page[$attr];
  1539. $value = @$page[$attr];
  1540. if (strncmp($attr, 'passwd', 6) == 0) {
  1541. $a = substr($attr, 6);
  1542. $value = '';
  1543. $setting = implode(' ',
  1544. preg_replace('/^(?!@|\\w+:).+$/', '****', (array)$page['=passwd'][$a]));
  1545. $pwsource = $page['=pwsource'][$a];
  1546. $FmtV['$PWSource'] = $pwsource;
  1547. $FmtV['$PWCascade'] = substr($pwsource, 8);
  1548. if ($pwsource == 'group' || $pwsource == 'site')
  1549. $setting = FmtPageName('$[(set by $PWSource)]', $pagename)." $setting";
  1550. if (strncmp($pwsource, 'cascade:', 8) == 0)
  1551. $setting = FmtPageName('$[(using $PWCascade password)]', $pagename);
  1552. }
  1553. $prompt = FmtPageName($p,$pagename);
  1554. echo "<tr><td>$prompt</td>
  1555. <td><input type='text' name='$attr' value='$value' /></td>
  1556. <td>$setting</td></tr>";
  1557. }
  1558. echo FmtPageName("</table><input type='submit' value='$[Save]' /></form>",
  1559. $pagename);
  1560. }
  1561. function HandleAttr($pagename, $auth = 'attr') {
  1562. global $PageAttrFmt,$PageStartFmt,$PageEndFmt;
  1563. $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT);
  1564. if (!$page) { Abort("?unable to read $pagename"); }
  1565. PCache($pagename,$page);
  1566. XLSDV('en', array('EnterAttributes' =>
  1567. "Enter new attributes for this page below. Leaving a field blank
  1568. will leave the attribute unchanged. To clear an attribute, enter
  1569. 'clear'."));
  1570. SDV($PageAttrFmt,"<div class='wikiattr'>
  1571. <h2 class='wikiaction'>$[{\$FullName} Attributes]</h2>
  1572. <p>$[EnterAttributes]</p></div>");
  1573. SDV($HandleAttrFmt,array(&$PageStartFmt,&$PageAttrFmt,
  1574. 'function:PrintAttrForm',&$PageEndFmt));
  1575. PrintFmt($pagename,$HandleAttrFmt);
  1576. }
  1577. function HandlePostAttr($pagename, $auth = 'attr') {
  1578. global $PageAttributes, $EnablePostAttrClearSession;
  1579. Lock(2);
  1580. $page = RetrieveAuthPage($pagename, $auth, true);
  1581. if (!$page) { Abort("?unable to read $pagename"); }
  1582. foreach($PageAttributes as $attr=>$p) {
  1583. $v = stripmagic(@$_POST[$attr]);
  1584. if ($v == '') continue;
  1585. if ($v=='clear') unset($page[$attr]);
  1586. else if (strncmp($attr, 'passwd', 6) != 0) $page[$attr] = $v;
  1587. else {
  1588. $a = array();
  1589. preg_match_all('/"[^"]*"|\'[^\']*\'|\\S+/', $v, $match);
  1590. foreach($match[0] as $pw)
  1591. $a[] = preg_match('/^(@|\\w+:)/', $pw) ? $pw
  1592. : crypt(preg_replace('/^([\'"])(.*)\\1$/', '$2', $pw));
  1593. if ($a) $page[$attr] = implode(' ',$a);
  1594. }
  1595. }
  1596. WritePage($pagename,$page);
  1597. Lock(0);
  1598. if (IsEnabled($EnablePostAttrClearSession, 1)) {
  1599. @session_start();
  1600. unset($_SESSION['authid']);
  1601. unset($_SESSION['authlist']);
  1602. $_SESSION['authpw'] = array();
  1603. }
  1604. Redirect($pagename);
  1605. exit;
  1606. }
  1607. function HandleLogoutA($pagename, $auth = 'read') {
  1608. global $LogoutRedirectFmt, $LogoutCookies;
  1609. SDV($LogoutRedirectFmt, '$FullName');
  1610. SDV($LogoutCookies, array());
  1611. @session_start();
  1612. $_SESSION = array();
  1613. if (isset($_COOKIE[session_name()]))
  1614. setcookie(session_name(), '', time()-43200, '/');
  1615. foreach ($LogoutCookies as $c)
  1616. if (isset($_COOKIE[$c])) setcookie($c, '', time()-43200, '/');
  1617. session_destroy();
  1618. Redirect(FmtPageName($LogoutRedirectFmt, $pagename));
  1619. }
  1620. function HandleLoginA($pagename, $auth = 'login') {
  1621. global $AuthId, $DefaultPasswords;
  1622. unset($DefaultPasswords['admin']);
  1623. $prompt = @(!$_POST['authpw'] || ($AuthId != $_POST['authid']));
  1624. $page = RetrieveAuthPage($pagename, $auth, $prompt, READPAGE_CURRENT);
  1625. Redirect($pagename);
  1626. }