Index: trunk/extensions/Lua/LuaWrapper.lua |
— | — | @@ -0,0 +1,213 @@ |
| 2 | +#!/usr/bin/env lua |
| 3 | +-- Lua parser extensions for MediaWiki - Wrapper for Lua interpreter |
| 4 | +-- |
| 5 | +-- @author Fran Rogers |
| 6 | +-- @package MediaWiki |
| 7 | +-- @addtogroup Extensions |
| 8 | +-- @license See 'COPYING' |
| 9 | +-- @file |
| 10 | + |
| 11 | +function make_sandbox() |
| 12 | + local function dummy(...) |
| 13 | + return nil |
| 14 | + end |
| 15 | + |
| 16 | + local function deepcopy(object, override) |
| 17 | + local lookup_table = {} |
| 18 | + local function _copy(object, override) |
| 19 | + if type(object) ~= "table" then |
| 20 | + return object |
| 21 | + elseif lookup_table[object] then |
| 22 | + return lookup_table[object] |
| 23 | + end |
| 24 | + local new_table = {} |
| 25 | + lookup_table[object] = new_table |
| 26 | + for index, value in pairs(object) do |
| 27 | + if override ~= nil then |
| 28 | + value = override |
| 29 | + end |
| 30 | + new_table[_copy(index)] = _copy(value, override) |
| 31 | + end |
| 32 | + return setmetatable(new_table, _copy(getmetatable(object), override)) |
| 33 | + end |
| 34 | + return _copy(object, override) |
| 35 | + end |
| 36 | + |
| 37 | + local env = {} |
| 38 | + |
| 39 | + local function _escape(s) |
| 40 | + s = string.gsub(s, "\\", "\\\\") |
| 41 | + s = string.gsub(s, "\'", "\\\'") |
| 42 | + return s |
| 43 | + end |
| 44 | + |
| 45 | + env._OUTPUT = "" |
| 46 | + |
| 47 | + local function writewrapper(...) |
| 48 | + local out = "" |
| 49 | + for n = 1, select("#", ...) do |
| 50 | + if out == "" then |
| 51 | + out = tostring(select(n, ...)) |
| 52 | + else |
| 53 | + out = out .. tostring(select(n, ...)) |
| 54 | + end |
| 55 | + end |
| 56 | + env._OUTPUT = env._OUTPUT .. out |
| 57 | + end |
| 58 | + |
| 59 | + local function outputwrapper(file) |
| 60 | + if file == nil then |
| 61 | + local file = {} |
| 62 | + file.close = dummy |
| 63 | + file.lines = dummy |
| 64 | + file.read = dummy |
| 65 | + file.flush = dummy |
| 66 | + file.seek = dummy |
| 67 | + file.setvbuf = dummy |
| 68 | + function file:write(...) writewrapper(...); end |
| 69 | + return file |
| 70 | + else |
| 71 | + return nil |
| 72 | + end |
| 73 | + end |
| 74 | + |
| 75 | + local function printwrapper(...) |
| 76 | + local out = "" |
| 77 | + for n = 1, select("#", ...) do |
| 78 | + if out == "" then |
| 79 | + out = tostring(select(n, ...)) |
| 80 | + else |
| 81 | + out = out .. '\t' .. tostring(select(n, ...)) |
| 82 | + end |
| 83 | + end |
| 84 | + env._OUTPUT =env._OUTPUT .. out .. "\n" |
| 85 | + end |
| 86 | + |
| 87 | + local oldloadstring = loadstring |
| 88 | + local function safeloadstring(s, chunkname) |
| 89 | + local f, message = oldloadstring(s, chunkname) |
| 90 | + if not f then |
| 91 | + return f, message |
| 92 | + end |
| 93 | + setfenv(f, getfenv(2)) |
| 94 | + return f |
| 95 | + end |
| 96 | + |
| 97 | + env.assert = _G.assert |
| 98 | + env.error = _G.error |
| 99 | + env._G = env |
| 100 | + env.ipairs = _G.ipairs |
| 101 | + env.loadstring = safeloadstring |
| 102 | + env.next = _G.next |
| 103 | + env.pairs = _G.pairs |
| 104 | + env.pcall = _G.pcall |
| 105 | + env.print = printwrapper |
| 106 | + env.write = writewrapper |
| 107 | + env.select = _G.select |
| 108 | + env.tonumber = _G.tonumber |
| 109 | + env.tostring = _G.tostring |
| 110 | + env.type = _G.type |
| 111 | + env.unpack = _G.unpack |
| 112 | + env._VERSION = _G._VERSION |
| 113 | + env.xpcall = _G.xpcall |
| 114 | + env.coroutine = deepcopy(_G.coroutine) |
| 115 | + env.string = deepcopy(_G.string) |
| 116 | + env.string.dump = nil |
| 117 | + env.table = deepcopy(_G.table) |
| 118 | + env.math = deepcopy(_G.math) |
| 119 | + env.io = {} |
| 120 | + env.io.write = writewrapper |
| 121 | + env.io.flush = dummy |
| 122 | + env.io.type = typewrapper |
| 123 | + env.io.output = outputwrapper |
| 124 | + env.io.stdout = outputwrapper() |
| 125 | + env.os = {} |
| 126 | + env.os.clock = _G.os.clock |
| 127 | + -- env.os.date = _G.os.date |
| 128 | + env.os.difftime = _G.os.difftime |
| 129 | + env.os.time = _G.os.time |
| 130 | + |
| 131 | + return env |
| 132 | +end |
| 133 | + |
| 134 | +function make_hook(maxlines, maxcalls, diefunc) |
| 135 | + local lines = 0 |
| 136 | + local calls = 0 |
| 137 | + function _hook(event, ...) |
| 138 | + if event == "call" then |
| 139 | + calls = calls + 1 |
| 140 | + if calls > maxcalls then |
| 141 | + diefunc("RECURSION_LIMIT") |
| 142 | + end |
| 143 | + elseif event == "return" then |
| 144 | + calls = calls - 1 |
| 145 | + elseif event == "line" then |
| 146 | + lines = lines + 1 |
| 147 | + if lines > maxlines then |
| 148 | + diefunc("LOC_LIMIT") |
| 149 | + end |
| 150 | + end |
| 151 | + end |
| 152 | + return _hook |
| 153 | +end |
| 154 | + |
| 155 | +function wrap(chunk, env, hook) |
| 156 | + local err = nil |
| 157 | + env._OUTPUT = "" |
| 158 | + setfenv(chunk, env) |
| 159 | + debug.sethook(hook, "crl") |
| 160 | + res = xpcall(chunk, function(s) err = s; end) |
| 161 | + debug.sethook() |
| 162 | + return res, err |
| 163 | +end |
| 164 | + |
| 165 | +function main() |
| 166 | + if #arg ~= 2 then |
| 167 | + io.stderr:write(string.format("usage: %s MAXLINES MAXCALLS\n", arg[0])) |
| 168 | + os.exit(1) |
| 169 | + end |
| 170 | + |
| 171 | + io.stdout:setvbuf("no") |
| 172 | + function _die(reason) |
| 173 | + io.stdout:write("'", reason, "', false\n.\n") |
| 174 | + os.exit(1) |
| 175 | + end |
| 176 | + hook = make_hook(tonumber(arg[1]), tonumber(arg[2]), |
| 177 | + _die) |
| 178 | + local env = make_sandbox() |
| 179 | + while true do |
| 180 | + local chunkstr = "" |
| 181 | + while true do |
| 182 | + local line = io.stdin:read("*l") |
| 183 | + if chunkstr == "" and line == nil then |
| 184 | + return nil |
| 185 | + elseif line == "." or line == nil then |
| 186 | + break |
| 187 | + elseif chunkstr ~= "" then |
| 188 | + chunkstr = chunkstr .. "\n" .. line |
| 189 | + else |
| 190 | + chunkstr = line |
| 191 | + end |
| 192 | + end |
| 193 | + |
| 194 | + local chunk |
| 195 | + local err |
| 196 | + chunk, err = loadstring(chunkstr) |
| 197 | + |
| 198 | + if err == nil then |
| 199 | + local res |
| 200 | + res, err = wrap(chunk, env, hook) |
| 201 | + end |
| 202 | + |
| 203 | + if err == nil then |
| 204 | + io.stdout:write("'", env._OUTPUT, "', true\n.\n") |
| 205 | + else |
| 206 | + io.stdout:write("'", err, "', false\n.\n") |
| 207 | + end |
| 208 | + end |
| 209 | + exit(0) |
| 210 | +end |
| 211 | + |
| 212 | +if arg ~= nil then |
| 213 | + main() |
| 214 | +end |
\ No newline at end of file |
Property changes on: trunk/extensions/Lua/LuaWrapper.lua |
___________________________________________________________________ |
Added: svn:executable |
1 | 215 | + |
Index: trunk/extensions/Lua/Lua.body.php |
— | — | @@ -0,0 +1,179 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Lua parser extensions for MediaWiki - Body |
| 5 | + * |
| 6 | + * @author Fran Rogers |
| 7 | + * @package MediaWiki |
| 8 | + * @addtogroup Extensions |
| 9 | + * @license See 'COPYING' |
| 10 | + * @file |
| 11 | + */ |
| 12 | + |
| 13 | +function efLua_ParserInit() { |
| 14 | + global $wgParser; |
| 15 | + $wgParser->setHook( 'lua', 'efLua_Render' ); |
| 16 | + return true; |
| 17 | +} |
| 18 | + |
| 19 | +function efLua_FunctionSetup() { |
| 20 | + global $wgParser; |
| 21 | + $wgParser->setFunctionHook('luaexpr', 'efLua_RenderExpr'); |
| 22 | +} |
| 23 | + |
| 24 | +function efLua_Magic(&$magicWords, $langCode) { |
| 25 | + $magicWords['luaexpr'] = array(0, 'luaexpr'); |
| 26 | + return true; |
| 27 | +} |
| 28 | + |
| 29 | +function efLua_BeforeTidy(&$parser, &$text) { |
| 30 | + if (!isset($wgLuaInterp)) { |
| 31 | + efLua_Cleanup(); |
| 32 | + } |
| 33 | + return TRUE; |
| 34 | +} |
| 35 | + |
| 36 | +function efLua_Render($input, $args, &$parser) { |
| 37 | + $arglist = ''; |
| 38 | + foreach ($args as $key => $value) |
| 39 | + $arglist .= (preg_replace('/\W/', '_', $key) . '=\'' . |
| 40 | + addslashes($parser->recursiveTagParse($value)) . |
| 41 | + '\';'); |
| 42 | + if ($arglist) { |
| 43 | + try { |
| 44 | + efLua_Eval($arglist); |
| 45 | + } catch (LuaError $e) { |
| 46 | + return $e->getMessage(); |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + try { |
| 51 | + return $parser->recursiveTagParse(efLua_Eval($input)); |
| 52 | + } catch (LuaError $e) { |
| 53 | + return $e->getMessage(); |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +function efLua_RenderExpr(&$parser, $param1 = FALSE) { |
| 58 | + if ($param1 == FALSE) |
| 59 | + return ''; |
| 60 | + try { |
| 61 | + return efLua_Eval("io.write($param1)"); |
| 62 | + } catch (LuaError $e) { |
| 63 | + return $e->getMessage(); |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +function efLua_Eval($input) { |
| 68 | + global $wgLuaExternalInterpreter, $wgLuaInterpDefunct, |
| 69 | + $wgLuaInterp, $wgLuaWrapperFile; |
| 70 | + if (isset($wgLuaInterpDefunct) && $wgLuaInterpDefunct) { |
| 71 | + return ''; |
| 72 | + } else if (isset($wgLuaExternalInterpreter)) { |
| 73 | + return efLua_EvalExternal($input); |
| 74 | + } else if (!class_exists('lua')) { |
| 75 | + throw new LuaError('pecl_notfound'); |
| 76 | + } |
| 77 | + |
| 78 | + if (!isset($wgLuaInterp)) { |
| 79 | + $wgLuaInterp = new lua; |
| 80 | + try { |
| 81 | + $wgLuaInterp->evaluatefile($wgLuaWrapperFile); |
| 82 | + |
| 83 | + } catch (Exception $e) { |
| 84 | + throw new LuaError('error_internal'); |
| 85 | + } |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +function efLua_Cleanup() { |
| 90 | + global $wgLuaExternalInterpreter, $wgLuaInterpDefunct, $wgLuaInterp; |
| 91 | + if (isset($wgLuaInterpDefunct) && $wgLuaInterpDefunct) { |
| 92 | + return FALSE; |
| 93 | + } else if (isset($wgLuaExternalInterpreter)) { |
| 94 | + return efLua_CleanupExternal(); |
| 95 | + } else if (isset($wgLuaInterp)) { |
| 96 | + $wgLuaInterpDefunct = TRUE; |
| 97 | + return TRUE; |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +function efLua_EvalExternal($input) { |
| 102 | + global $wgLuaExternalInterpreter, $wgLuaProc, $wgLuaPipes, |
| 103 | + $wgLuaWrapperFile, $wgLuaMaxLines, $wgLuaMaxCalls, |
| 104 | + $wgLuaMaxTime; |
| 105 | + if (!isset($wgLuaProc)) { |
| 106 | + $wgLuaInterp = TRUE; |
| 107 | + $luacmd = "$wgLuaExternalInterpreter $wgLuaWrapperFile $wgLuaMaxLines $wgLuaMaxCalls"; |
| 108 | + $wgLuaProc = proc_open($luacmd, |
| 109 | + array(0 => array('pipe', 'r'), |
| 110 | + 1 => array('pipe', 'w')), |
| 111 | + $wgLuaPipes, NULL, NULL); |
| 112 | + if (!is_resource($wgLuaProc)) |
| 113 | + throw new LuaError('interp_notfound'); |
| 114 | + stream_set_blocking($wgLuaPipes[0], 0); |
| 115 | + stream_set_blocking($wgLuaPipes[1], 0); |
| 116 | + stream_set_write_buffer($wgLuaPipes[0], 0); |
| 117 | + stream_set_write_buffer($wgLuaPipes[1], 0); |
| 118 | + } |
| 119 | + |
| 120 | + $input = trim(preg_replace('/(?<=\n|^)\.(?=\n|$)/', '. --', $input)); |
| 121 | + fwrite($wgLuaPipes[0], "$input\n.\n"); |
| 122 | + fflush($wgLuaPipes[0]); |
| 123 | + |
| 124 | + $res = ''; |
| 125 | + $read = array($wgLuaPipes[1]); |
| 126 | + $write = NULL; |
| 127 | + $except = NULL; |
| 128 | + while (!feof($wgLuaPipes[1])) { |
| 129 | + if (false === ($num_changed_streams = |
| 130 | + @stream_select($read, $write, $except, |
| 131 | + $wgLuaMaxTime))) { |
| 132 | + efLua_CleanupExternal(); |
| 133 | + throw new LuaError('overflow_time'); |
| 134 | + } |
| 135 | + $line = fgets($wgLuaPipes[1]); |
| 136 | + if ($line == ".\n") |
| 137 | + break; |
| 138 | + $res .= $line; |
| 139 | + } |
| 140 | + |
| 141 | + if (preg_match('/^\'(.*)\', (true|false)$/s', trim($res), $match) != 1) { |
| 142 | + efLua_CleanupExternal(); |
| 143 | + throw new LuaError('error_internal'); |
| 144 | + } |
| 145 | + |
| 146 | + $out = $match[1]; |
| 147 | + if ($match[2] == 'true') { |
| 148 | + return (trim($out) != '') ? $out : ''; |
| 149 | + } else { |
| 150 | + if ($out == 'RECURSION_LIMIT') { |
| 151 | + efLua_CleanupExternal(); |
| 152 | + throw new LuaError('overflow_recursion'); |
| 153 | + } else if ($out == 'LOC_LIMIT') { |
| 154 | + efLua_CleanupExternal(); |
| 155 | + throw new LuaError('overflow_loc'); |
| 156 | + } else { |
| 157 | + $out = preg_replace('/^\[.+?\]:(.+?):/', '$1:', $out); |
| 158 | + throw new LuaError('error', $out); |
| 159 | + } |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +function efLua_CleanupExternal() { |
| 164 | + global $wgLuaExternalInterpreter, $wgLuaInterpDefunct, |
| 165 | + $wgLuaProc, $wgLuaPipes; |
| 166 | + if (isset($wgLuaProc)) { |
| 167 | + fclose($wgLuaPipes[0]); |
| 168 | + fclose($wgLuaPipes[1]); |
| 169 | + proc_close($wgLuaProc); |
| 170 | + } |
| 171 | + $wgLuaInterpDefunct = TRUE; |
| 172 | + return TRUE; |
| 173 | +} |
| 174 | + |
| 175 | +class LuaError extends Exception { |
| 176 | + public function __construct($msg, $parameter = ''){ |
| 177 | + wfLoadExtensionMessages( 'Lua' ); |
| 178 | + $this->message = '<strong class="error">' . wfMsgForContent( "lua_$msg", htmlspecialchars( $parameter ) ) . '</strong>'; |
| 179 | + } |
| 180 | +} |
Property changes on: trunk/extensions/Lua/Lua.body.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 181 | + native |
Index: trunk/extensions/Lua/Lua.i18n.php |
— | — | @@ -0,0 +1,22 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Lua parser extensions for MediaWiki - Internationalization |
| 5 | + * |
| 6 | + * @author Fran Rogers |
| 7 | + * @package MediaWiki |
| 8 | + * @addtogroup Extensions |
| 9 | + * @license See 'COPYING' |
| 10 | + * @file |
| 11 | + */ |
| 12 | + |
| 13 | +$messages = array(); |
| 14 | + |
| 15 | +$messages['en'] = array( |
| 16 | + 'lua_desc' => 'Extends the parser with support for embedded blocks of [http://www.lua.org/ Lua] code', |
| 17 | + 'lua_error' => 'Error on line $1', |
| 18 | + 'lua_interp_notfound' => 'Lua interpreter not found', |
| 19 | + 'lua_error_internal' => 'Internal error', |
| 20 | + 'lua_overflow_recursion' => 'Recursion limit reached', |
| 21 | + 'lua_overflow_loc' => 'Maximum lines of code limit reached', |
| 22 | + 'lua_overflow_time' => 'Maximum execution time reached', |
| 23 | +); |
Property changes on: trunk/extensions/Lua/Lua.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 24 | + native |
Index: trunk/extensions/Lua/Lua.php |
— | — | @@ -0,0 +1,47 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Lua parser extensions for MediaWiki |
| 5 | + * |
| 6 | + * @author Fran Rogers |
| 7 | + * @package MediaWiki |
| 8 | + * @addtogroup Extensions |
| 9 | + * @license See 'COPYING' |
| 10 | + * @file |
| 11 | + */ |
| 12 | + |
| 13 | +$wgExtensionCredits['parserhook'][] = array( |
| 14 | + 'name' => 'Lua parser extensions', |
| 15 | + 'author' => 'Fran Rogers', |
| 16 | + 'svn-date' => '$LastChangedDate$', |
| 17 | + 'svn-revision' => '$LastChangedRevision$', |
| 18 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:Lua', |
| 19 | + 'description' => 'Extends the parser with support for embedded blocks ofLua code', |
| 20 | + 'descriptionmsg' => 'lua_desc', |
| 21 | +); |
| 22 | + |
| 23 | +$wgExtensionMessagesFiles['Lua'] = dirname(__FILE__) . '/Lua.i18n.php'; |
| 24 | +require_once(dirname(__FILE__) . '/Lua.body.php'); |
| 25 | +$wgLuaWrapperFile = dirname(__FILE__) . '/LuaWrapper.lua'; |
| 26 | + |
| 27 | +if (!isset($wgLuaExternalInterpreter)) |
| 28 | + $wgLuaExternalInterpreter = FALSE; |
| 29 | +if (!isset($wgLuaExternalInterpreter)) |
| 30 | + $wgLuaExternalInterpreter = FALSE; |
| 31 | +if (!isset($wgLuaMaxLines)) |
| 32 | + $wgLuaMaxLines = 1000000; |
| 33 | +if (!isset($wgLuaMaxCalls)) |
| 34 | + $wgLuaMaxCalls = 2000; |
| 35 | +if (!isset($wgLuaMaxTime)) |
| 36 | + $wgLuaMaxTime = 5; |
| 37 | + |
| 38 | +# Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW versions, as per r35980 |
| 39 | +if (defined('MW_SUPPORTS_PARSERFIRSTCALLINIT')) { |
| 40 | + $wgHooks['ParserFirstCallInit'][] = 'efLua_ParserInit'; |
| 41 | +} else { // Otherwise do things the old fashioned way |
| 42 | + $wgExtensionFunctions[] = 'efLua_ParserInit'; |
| 43 | +} |
| 44 | +# Define a setup function |
| 45 | +$wgExtensionFunctions[] = 'efLua_FunctionSetup'; |
| 46 | +# Add a hook to initialise the magic word |
| 47 | +$wgHooks['LanguageGetMagic'][] = 'efLua_Magic'; |
| 48 | +$wgHooks['ParserBeforeTidy'][] = 'efLua_BeforeTidy'; |
Property changes on: trunk/extensions/Lua/Lua.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 49 | + native |
Index: trunk/extensions/Lua/COPYING |
— | — | @@ -0,0 +1,18 @@ |
| 2 | +Lua parser extensions for MediaWiki |
| 3 | +Copyright (C) 2008 Fran Rogers |
| 4 | + |
| 5 | +This software is provided 'as-is', without any express or implied |
| 6 | +warranty. In no event will the authors be held liable for any damages |
| 7 | +arising from the use of this software. |
| 8 | + |
| 9 | +Permission is granted to anyone to use this software for any purpose, |
| 10 | +including commercial applications, and to alter it and redistribute it |
| 11 | +freely, subject to the following restrictions: |
| 12 | + |
| 13 | +1. The origin of this software must not be misrepresented; you must not |
| 14 | + claim that you wrote the original software. If you use this software |
| 15 | + in a product, an acknowledgment in the product documentation would be |
| 16 | + appreciated but is not required. |
| 17 | +2. Altered source versions must be plainly marked as such, and must not be |
| 18 | + misrepresented as being the original software. |
| 19 | +3. This notice may not be removed or altered from any source distribution. |
Index: trunk/extensions/Lua/README |
— | — | @@ -0,0 +1,3 @@ |
| 2 | +README forthcoming. :) |
| 3 | + |
| 4 | +See http://www.mediawiki.org/wiki/Extension:Lua for now. |
\ No newline at end of file |