Difference between revisions of "Xmlwiki.php"
(XSLT test must be before XML) |
(Fixing security problem with search) |
||
Line 174: | Line 174: | ||
function xwOutputHook() { | function xwOutputHook() { | ||
− | global $wgUser, $xwUserName, $wgOut, $xwDebug, $xwIsProperties, $xwIsSystem; | + | global $wgUser, $xwUserName, $wgOut, $xwDebug, $xwIsProperties, $xwIsSystem, $xwIsAdmin; |
global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwEdit, $action; | global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwEdit, $action; | ||
if ($action == 'raw') return; | if ($action == 'raw') return; | ||
Line 208: | Line 208: | ||
} | } | ||
} | } | ||
− | elseif ($xwEdit) { | + | elseif ( $xwEdit ) { |
# Editing normally, apply transforms in edit-list | # Editing normally, apply transforms in edit-list | ||
− | xwReduceTransformStack($xwArticle, $xwArticleProperties, $xwArticleTitle, 'edit', 'OUTPUT-HOOK'); | + | xwReduceTransformStack( $xwArticle, $xwArticleProperties, $xwArticleTitle, 'edit', 'OUTPUT-HOOK' ); |
} | } | ||
− | if ($xwDebug) { | + | if ( $xwDebug ) { |
global $xwParserHookCalled, $xwSkinHookCalled; | global $xwParserHookCalled, $xwSkinHookCalled; | ||
− | if (!$xwParserHookCalled) xwMessage('PARSER-HOOK was not called','green'); | + | if ( !$xwParserHookCalled ) xwMessage( 'PARSER-HOOK was not called', 'green' ); |
− | if (!$xwSkinHookCalled) xwMessage('SKIN-HOOK was not called','green'); | + | if ( !$xwSkinHookCalled ) xwMessage( 'SKIN-HOOK was not called', 'green' ); |
} | } | ||
# Insert stylesheets and messages into output | # Insert stylesheets and messages into output | ||
− | xwReplaceTokens($xwArticle); | + | xwReplaceTokens( $xwArticle ); |
+ | |||
+ | # Permhack: remove private info if search result | ||
+ | if ( $_REQUEST['search'] && !$xwIsAdmin ) $xwArticle = preg_replace( | ||
+ | '/(<li><a href.+?>(.+?)<\\/a>.+?<\\/li>)/ise', | ||
+ | 'xwArticleAccess("$2","read")?"$1":""', | ||
+ | $xwArticle | ||
+ | ); | ||
# Output final result | # Output final result | ||
Line 230: | Line 237: | ||
# PRE-PARSER HOOK | # PRE-PARSER HOOK | ||
− | function xwPreParserHook(&$text) { | + | function xwPreParserHook( &$text ) { |
global $xwArticleTitle, $xwArticle, $xwArticleProperties, $xwSave; | global $xwArticleTitle, $xwArticle, $xwArticleProperties, $xwSave; | ||
global $xwParserHookCalled, $xwDebug, $xwReadable, $xwScript; | global $xwParserHookCalled, $xwDebug, $xwReadable, $xwScript; | ||
− | if ($xwDebug) xwMessage('PREPARSER-HOOK:','green'); | + | if ( $xwDebug ) xwMessage( 'PREPARSER-HOOK:', 'green' ); |
$xwParserHookCalled = true; | $xwParserHookCalled = true; | ||
# Save clears all output, but we need to parse it for save-transforms | # Save clears all output, but we need to parse it for save-transforms | ||
− | if ($xwSave) $text = $_REQUEST['wpTextbox1']; | + | if ( $xwSave ) $text = $_REQUEST['wpTextbox1']; |
# If this text-fragment is our article, apply transforms | # If this text-fragment is our article, apply transforms | ||
− | if ($xwSave or strncmp($text, $xwArticle, 100) == 0 or strncmp($text, xwArticleContent($xwArticleTitle, false), 100) == 0) { | + | if ( $xwSave or strncmp( $text, $xwArticle, 100 ) == 0 or strncmp( $text, xwArticleContent( $xwArticleTitle, false ), 100 ) == 0 ) { |
− | if ($xwDebug) xwMessage("Matching text-fragment intercepted."); | + | if ( $xwDebug ) xwMessage( "Matching text-fragment intercepted." ); |
− | if (!$xwReadable) { | + | if ( !$xwReadable ) { |
− | xwMessage('Sorry, article not readable!','red'); | + | xwMessage( 'Sorry, article not readable!', 'red' ); |
$text = $xwArticle = "<a href=\"$xwScript?title=Special:Userlogin\">Please Login</a>"; | $text = $xwArticle = "<a href=\"$xwScript?title=Special:Userlogin\">Please Login</a>"; | ||
return false; | return false; | ||
Line 250: | Line 257: | ||
# - domificate first if xml | # - domificate first if xml | ||
# - undomificate after if still an object | # - undomificate after if still an object | ||
− | if (preg_match("/^<\\?xml/i", $text)) xwDomificateArticle($text, 'PreParserHook/text'); | + | if ( preg_match("/^<\\?xml/i", $text) ) xwDomificateArticle( $text, 'PreParserHook/text' ); |
− | xwReduceTransformStack($text, $xwArticleProperties, $xwArticleTitle, 'data', 'PREPARSER-HOOK'); | + | xwReduceTransformStack( $text, $xwArticleProperties, $xwArticleTitle, 'data', 'PREPARSER-HOOK' ); |
− | if (is_object($text)) xwUndomificateArticle($text, $xwArticleTitle); | + | if ( is_object($text) ) xwUndomificateArticle( $text, $xwArticleTitle ); |
} | } | ||
− | elseif ($xwDebug) xwMessage('Parsing other content'); | + | elseif ( $xwDebug ) xwMessage( 'Parsing other content' ); |
# Get language and return true back to parser if lang wasn't ours | # Get language and return true back to parser if lang wasn't ours | ||
− | xwGetProperty($xwArticleProperties, 'language', $language); | + | xwGetProperty( $xwArticleProperties, 'language', $language ); |
− | if ($xwDebug) xwMessage('Content language determined as '.($language ? strtoupper($language) : 'WIKI')); | + | if ($xwDebug) xwMessage( 'Content language determined as '.($language ? strtoupper($language) : 'WIKI') ); |
return $language == ''; | return $language == ''; | ||
} | } | ||
Line 266: | Line 273: | ||
# POST-PARSER HOOK | # POST-PARSER HOOK | ||
− | function xwPostParserHook(&$text) { | + | function xwPostParserHook( &$text ) { |
global $xwArticleTitle, $xwArticleProperties, $xwSave, $xwDebug; | global $xwArticleTitle, $xwArticleProperties, $xwSave, $xwDebug; | ||
− | if (!$xwSave) return; | + | if ( !$xwSave ) return; |
− | if ($xwDebug) xwMessage('POSTPARSER-HOOK:','green'); | + | if ( $xwDebug ) xwMessage( 'POSTPARSER-HOOK:','green' ); |
# Apply view transforms first | # Apply view transforms first | ||
− | xwReduceTransformStack($text, $xwArticleProperties, $xwArticleTitle, 'view', 'POSTPARSER-HOOK' | + | xwReduceTransformStack( $text, $xwArticleProperties, $xwArticleTitle, 'view', 'POSTPARSER-HOOK' ); |
− | |||
− | |||
− | |||
# Now apply the save-stack | # Now apply the save-stack | ||
− | xwReduceTransformStack($text, $xwArticleProperties, $xwArticleTitle, 'save', 'POSTPARSER-HOOK'); | + | xwReduceTransformStack( $text, $xwArticleProperties, $xwArticleTitle, 'save', 'POSTPARSER-HOOK' ); |
# Output should be empty for save | # Output should be empty for save | ||
Line 290: | Line 294: | ||
# If attempted xml encountered, try to domificate and transform | # If attempted xml encountered, try to domificate and transform | ||
# - Raw requests don't get here | # - Raw requests don't get here | ||
− | function xwSkinHook(&$tmpl) { | + | function xwSkinHook( &$tmpl ) { |
global $xwArticle, $xwArticleTitle, $xwArticleProperties, $xwRaw; | global $xwArticle, $xwArticleTitle, $xwArticleProperties, $xwRaw; | ||
Line 296: | Line 300: | ||
# Extract article from existing template structure | # Extract article from existing template structure | ||
− | if ($xwDebug) xwMessage('SKIN-HOOK:','green'); | + | if ( $xwDebug ) xwMessage( 'SKIN-HOOK:', 'green' ); |
$xwSkinHookCalled = true; | $xwSkinHookCalled = true; | ||
$xwTemplate = $tmpl; | $xwTemplate = $tmpl; | ||
− | if ($xwReadable) $xwArticle = $xwTemplate->data['bodytext']; | + | if ( $xwReadable ) $xwArticle = $xwTemplate->data['bodytext']; |
# Apply view-transforms | # Apply view-transforms | ||
Line 305: | Line 309: | ||
# - "default-skin.xslt" transforms XML to HTML with table-layout | # - "default-skin.xslt" transforms XML to HTML with table-layout | ||
# - "default-skin.css" transforms HTML with design | # - "default-skin.css" transforms HTML with design | ||
− | xwReduceTransformStack($xwArticle, $xwArticleProperties, $xwArticleTitle, 'view', 'SKIN-HOOK'); | + | xwReduceTransformStack( $xwArticle, $xwArticleProperties, $xwArticleTitle, 'view', 'SKIN-HOOK' ); |
} | } | ||
Line 312: | Line 316: | ||
# ARTICLE FUNCTIONS | # ARTICLE FUNCTIONS | ||
# - these all use super-globals since they can be called from within evals | # - these all use super-globals since they can be called from within evals | ||
+ | |||
+ | # Return true if passed title readable/writable by current user | ||
+ | function xwArticleAccess( $title, $perm = 'read' ) { | ||
+ | global $xwIsAdmin, $xwUserGroups; | ||
+ | if ( !$access = $xwIsAdmin ) { | ||
+ | $access = xwGetListByTagname( xwArticleProperties( $title ), $perm ) + array( 'anyone' ); | ||
+ | $access = count( array_intersect( $access, $xwUserGroups ) ); | ||
+ | } | ||
+ | return $access; | ||
+ | } | ||
# Retreive wiki-article as raw text | # Retreive wiki-article as raw text | ||
− | function xwArticleContent($articleTitle, $secure = true) { | + | function xwArticleContent( $articleTitle, $secure = true ) { |
global $xwArticleCache, $xwDebug, $xwIsAdmin, $xwUserGroups; | global $xwArticleCache, $xwDebug, $xwIsAdmin, $xwUserGroups; | ||
if ($xwDebug) xwMessage("xwArticleContent(\"$articleTitle\")",'blue'); | if ($xwDebug) xwMessage("xwArticleContent(\"$articleTitle\")",'blue'); |
Revision as of 23:23, 12 March 2006
- xmlWiki - MediaWiki XML Hack
- Nad - Started: 2005-05-18
- Exit if not included from index.php
defined('MEDIAWIKI') or die('xmlwiki.php must be included from MediaWiki\'s index.php!'); if (php(3)) die('xmlWiki cannot run on less than PHP4!');
- Add the rest of the hooks
$wgHooks['PreParser'][] = 'xwPreParserHook'; $wgHooks['ParserBeforeStrip'][] = 'xwPreParserHook'; $wgHooks['PostParser'][] = 'xwPostParserHook'; $wgHooks['ParserAfterTidy'][] = 'xwPostParserHook'; $wgHooks['Input'][] = 'xwInputHook'; $wgHooks['Output'][] = 'xwOutputHook';
- System globals
$xwDebug = false; $xwMessages = array(); $xwArticleCache = array(); $xwStyleSheets = array(); $xwTemplate = null; $xwParserHookCalled = false; $xwSkinHookCalled = false; $xwMsgToken = ; $xwCssToken = ; $xwScript = $wgScript;
- User globals
$xwUserName = ucwords($wgUser->mName); $xwUserSYS = null; $xwUserGroups = array($xwUserName, 'anyone'); $xwEdit = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'edit'); $xwView = (!isset($_REQUEST['action']) || (isset($_REQUEST['action']) && ($_REQUEST['action'] == 'view'))); $xwRaw = isset($_REQUEST['action']) && ($_REQUEST['action'] == 'raw'); $xwPreview = isset($_REQUEST['wpPreview']); $xwSave = isset($_REQUEST['wpSave']);
- Article globals
$xwArticleTitle = $wgTitle->getPrefixedText(); $xwArticle = $xwPreview ? $wgRequest->gettext('wpTextbox1') : $wgArticle->getContent(false); $xwArticleProperties = null; $xwIsProperties = preg_match('/^xml:.+$/i', $xwArticleTitle); $xwIsSystem = preg_match('/^sys:.+$/i', $xwArticleTitle); $xwIsUser = preg_match('/^user:.+$/i', $xwArticleTitle); $xwIsSpecial = preg_match('/^special:.+$/i', $xwArticleTitle); $xwIsAdmin = false; $xwArticleReadableBy = null; $xwArticleWritableBy = null;
- Get default properties article
$xwDefaultProperties = xwDomificateArticle(xwArticleContent('default-properties.xml'), 'defaults');
- Get user-system-article and extract users' groups from it if any
$xwUserSYS = xwArticleContent("sys:$xwUserName"); xwDomificateArticle($xwUserSYS, "sys:$xwUserName"); $xwUserGroups = array_merge($xwUserGroups, xwGetListByTagname($xwUserSYS, 'groups'));
- Read user-prefs
xwGetProperty($xwUserSYS, 'xpath:/user/debug', $xwDebug);
- Get article-meta-file and extract article-perms
if ($xwIsSystem) $xwArticleReadableBy = $xwArticleWritableBy = array('admin'); else { # Get xml:article (self if already xml:*) if ($xwArticleProperties = $xwIsProperties ? xwDomificateArticle($xwArticle) : xwArticleProperties($xwArticleTitle))
# Merge default-properties into article-properties object if (is_object($xwArticleProperties)) xwMergeDOM($xwArticleProperties, $xwDefaultProperties); else $xwArticleProperties = $xwDefaultProperties;
$xwArticleReadableBy = xwGetListByTagname($xwArticleProperties, 'read'); $xwArticleWritableBy = xwGetListByTagname($xwArticleProperties, 'write'); if ($xwIsProperties) $xwArticleProperties = false; } if (!is_object($xwArticleProperties)) $xwArticleProperties = $xwDefaultProperties; if ($xwIsSpecial || !count($xwArticleReadableBy)) $xwArticleReadableBy = array('anyone'); if (!count($xwArticleWritableBy)) $xwArticleWritableBy = array('anyone');
- If no language specified in properties, guess from name and content
if (!xwGetProperty($xwArticleProperties, 'language', $xwLanguage)) xwSetProperty($xwArticleProperties, 'language', xwArticleType($xwArticleTitle, $xwArticle));
- Set perms for this request
if ($xwDebug) xwMessage('PERMISSIONS:','green'); if (in_array('admin', $xwUserGroups)) $xwIsAdmin = $xwReadable = $xwWritable = true; else { $xwReadable = 0 < count(array_intersect($xwArticleReadableBy, $xwUserGroups)); $xwWritable = 0 < count(array_intersect($xwArticleWritableBy, $xwUserGroups)); } if ($xwDebug) { xwMessage('Groups: '.join(', ', $xwUserGroups)); xwMessage('Readable ('.($xwReadable?'yes':'no').'): '.join(', ', $xwArticleReadableBy)); xwMessage('Writable ('.($xwWritable?'yes':'no').'): '.join(', ', $xwArticleWritableBy)); }
- Divert to access-denied article if not readable
if (!$xwReadable) { $action = 'view'; $xwSave = $xwEdit = false; }
- Handle security for Move
if (!$xwIsAdmin and ($target = $_REQUEST['target']) ) { if ($xwArticleProperties = new Article(Title::newFromText("xml:$target"))) $xwArticleProperties = $xwArticleProperties->getContentWithoutUsingSoManyDamnGlobals(); $writableBy = xwGetListByTagname(xwDomificateArticle($xwArticleProperties), 'write'); if (!count($writableBy)) $writableBy = array('anyone'); if (!count(array_intersect($writableBy, $xwUserGroups))) { $xwArticleTitle = $target; xwMessage("Sorry article \"$xwArticleTitle\" not movable!",'red'); $wgArticle = new Article($wgTitle = Title::newFromText($target)); $xwArticle = $wgArticle->getContent(false); } }
- Apply init transforms
xwReduceTransformStack( $xwArticle, $xwArticleProperties, $xwArticleTitle, 'init', 'INIT-HOOK' );
- ---------------------------------------------------------------------------------------------------------------------- #
- INPUT HOOK
- Parse and process input from forms
function xwInputHook() {
global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwWritable, $_REQUEST; global $xwDebug, $xwSave, $xwEdit, $action, $xwUserName, $wgArticle, $wgTitle; global $xwUserGroups, $xwIsProperties, $xwIsSystem, $xwIsAdmin; if ($xwDebug) xwMessage('INPUT-HOOK:','green');
# If not writable, change action to view if ( !$xwWritable && !in_array($action, array('view','history','raw')) ) { xwMessage('Sorry, article not writable, action changed to "view".', 'red'); $action = 'view'; $xwSave = $xwEdit = false; }
# Scan POST and apply any XPath inputs foreach ( $_REQUEST as $query => $value ) { if ( ereg('^xpath.3A.+.3A', $query) ) $query = urldecode( $query ); if ( ereg('^xpath:.+:', $query) ) xwSetProperty( $xwArticleProperties, $query, $value ); }
# if saving a pseudo-namespace... if ($xwSave) { if ( $xwIsProperties || $xwIsSystem ) { # wpTextbox1 should be valid XML to be saved $tb = $_REQUEST['wpTextbox1']; xwDomificateArticle( $tb, 'POST-DATA' ); if ( !is_object($tb) ) { xwMessage('A meta-article must be valid XML! article not saved.', 'red'); $action = 'view'; $xwSave = $xwEdit = false; } elseif ( $xwIsProperties && !$xwIsAdmin ) { foreach ( xwGetListByTagname($tb, 'write') as $perm ) { if ( !in_array( $perm, $xwUserGroups ) ) { xwMessage("Only members of \"$perm\" can set permissions for \"$perm\"! article not saved.", 'red'); $action = 'view'; $xwSave = $xwEdit = false; } } } } }
}
- ---------------------------------------------------------------------------------------------------------------------- #
- OUTPUT HOOK
- Post-process and output article
function xwOutputHook() {
global $wgUser, $xwUserName, $wgOut, $xwDebug, $xwIsProperties, $xwIsSystem, $xwIsAdmin; global $xwArticle, $xwArticleProperties, $xwArticleTitle, $xwEdit, $action; if ($action == 'raw') return; if ($xwDebug) xwMessage('OUTPUT-HOOK:','green');
# Activate the skin-hook and generate wiki's output $wgUser->setOption('skin', 'xwskin'); $wgOut->output();
# Editing article if ($xwEdit && preg_match("/^(.*<textarea .+?>)\\s*(<\\/textarea>.*)$/s", $xwArticle, $m)) { # If editing an empty sys:article or xml:article, add default content if ($xwIsSystem) { $xwArticle = $m[1]."<?xml version=\"1.0\" standalone=\"yes\"?>\n"; $xwArticle .= "<!DOCTYPE xmlwiki:user SYSTEM \"xmlwiki-user.dtd\">\n"; $xwArticle .= "<user>\n\t<groups></groups>\n</user>\n".$m[2]; xwMessage('Note: There is currently no content for this system article, default content has been generated','red'); } elseif ($xwIsProperties) { $a = xwArticleContent( str_replace( 'Xml:', , $xwArticleTitle ) ); if ( preg_match("/\\[\\[Category:(.+?)]]/", $a, $n) && $xwArticle = xwArticleProperties('Category:'.$n[1]) ) { xwUndomificateArticle( $xwArticle ); xwMessage('Note: This article has no properties, default content has been inherited from Xml:'.$n[1], 'red'); } else { xwMessage('Note: This article has no properties, a general default has been generated', 'red'); $xwArticle = "<?xml version=\"1.0\" standalone=\"yes\"?>\n"; $xwArticle .= "<!DOCTYPE xmlwiki:properties SYSTEM \"xmlwiki-properties.dtd\">\n"; $xwArticle .= "<properties>\n\t<read>anyone</read>\n\t<write>$xwUserName</write>\n"; $xwArticle .= "\t<language></language>\n\t<category></category>\n\t\n\t<view></view>\n\t<edit></edit>\n\t<save></save>\n</properties>\n"; } $xwArticle = $m[1].$xwArticle.$m[2]; } } elseif ( $xwEdit ) { # Editing normally, apply transforms in edit-list xwReduceTransformStack( $xwArticle, $xwArticleProperties, $xwArticleTitle, 'edit', 'OUTPUT-HOOK' ); }
if ( $xwDebug ) { global $xwParserHookCalled, $xwSkinHookCalled; if ( !$xwParserHookCalled ) xwMessage( 'PARSER-HOOK was not called', 'green' ); if ( !$xwSkinHookCalled ) xwMessage( 'SKIN-HOOK was not called', 'green' ); }
# Insert stylesheets and messages into output xwReplaceTokens( $xwArticle );
# Permhack: remove private info if search result if ( $_REQUEST['search'] && !$xwIsAdmin ) $xwArticle = preg_replace(
'/(
- ---------------------------------------------------------------------------------------------------------------------- #
- PRE-PARSER HOOK
- ---------------------------------------------------------------------------------------------------------------------- #
- POST-PARSER HOOK
- ---------------------------------------------------------------------------------------------------------------------- #
- SKIN HOOK
- If attempted xml encountered, try to domificate and transform
- - Raw requests don't get here
- ---------------------------------------------------------------------------------------------------------------------- #
- ARTICLE FUNCTIONS
- - these all use super-globals since they can be called from within evals
- Return true if passed title readable/writable by current user
- Retreive wiki-article as raw text
- Return properties object from title
- - returns self if already a properties article
- Decide kind of article from content and title
- Convert passed article to a DOM object
- - Article is unchanged if not valid XML
- Reduce article to a string if it's a DOM object
- - ie if all xslt's had xml-output-method
- ---------------------------------------------------------------------------------------------------------------------- #
- TRANSFORM FUNCTIONS
- Apply all transforms in passed stack
- Apply a transform
- Apply PHP-transform if article perms are only writable by admin or dev
- Append CSS stylesheet link to output
- Apply XSLT to article-dom
- - if article is not and object, the XSLT will be appended to the stylesheet-ref list like a CSS
- - if output-method is html, article will get stringified and language updated
- ---------------------------------------------------------------------------------------------------------------------- #
- DOM FUNCTIONS
- Return result set of an XPath query on passed DOM-object
- - Syntax: xpath:XPathQuery:[[@]node][+] = value
- - if "node" exists, then a new element (or att if "@node") is created for the value
- - if "+" exists, content is appended, else replaced
- Return array of comma-separated-items in element content
- - if more than one element all are split and appended into the list
- Merge the two passed DOM objects
- - appends, does not overwrite
- Remove all occourences of a certain element
- ---------------------------------------------------------------------------------------------------------------------- #
- UTILITY FUNCTIONS
- Extract error-messages from captured output and put in proper message-queue
- Add message to queue
There are Messages
$article = str_replace($xwMsgToken, $msg, $article); } }
- Print list of passed object's methods and properties
function xwCheckoutObject($obj) {
if (is_object($obj)) {
print strtoupper("
$obj properties:
");
foreach (get_object_vars($obj) as $k=>$v) print htmlentities("$k => $v")."
";
print strtoupper("
$obj methods:
");
foreach (get_class_methods($obj) as $k=>$v) print "$v
";
}
if (is_array($obj)) {
print strtoupper("
Array content:
");
foreach ($obj as $k=>$v) print "$k => $v
";
}
die;
}
- Return true if PHP major version equals passed int
- - needed due to different DOM implimentation on 4 and 5 (and none on 3)
function php($ver) { return substr(phpversion(),0,1) == "$ver"; }