local assert, type, pairs = assert, type, pairs
local string = string
local ipairs = ipairs

module((...))

local function MakeSanitizerFunction(substitutions)
	assert(type(substitutions) == "table")
	
	local match = "["
	for k,v in pairs(substitutions) do
		match = match .. k
	end
	match = match .. "]"
	
	return function(text)
		return (string.gsub(text, match, function(char)
			return substitutions[char]
		end))
	end
end

-- javascript sanitizer
ToJs = MakeSanitizerFunction{
	["\""] = [[\"]],
	["'"] = [[\']]
}

-- HTML sanitizer
--http://www.w3.org/TR/xhtml1/#C_16
ToHtml = MakeSanitizerFunction{
	["<"] = [[&lt;]],
	[">"] = [[&gt;]],
	["\""] = [[&quot;]],
	["&"] = [[&amp;]],
	["'"] = [[&#39;]]
}

--[[---------------------------------------------------------------------------------
	Elimino del html:
		1) Los siguientes tags:
			<base/>, <link>, <script></script>, <iframe></iframe>
			Aquí se eliminan tando cuando se abre un tag y no se vuelve a cerrar <link>, o se abre y cierra en el mismo tag <base />,
			como cuando se abre y cierra con otro taga <script></script>

		2) Todos los eventos javascript y alungos atributos de los tags html por ej:
			onclick, onerror, onload, ..., ..., etc.
			href, ..., etc.

		3) Las posibles ejecuciones de javascript desde CSS, por ejemplo:
			background-color:expression("yellow";alert('peteco')); o 
			behavior: url("css/test.htc"); -- ejecucion de archivos htc
--]]
function CleanUpHtml(text, tagList)
	if not text then
		return ''
	end
	--Esta funcion elimina por completo el tag recidido por parémtro y su contenido.
	local function cleanUpTags(text, tagList)
		-- si tag = "script", regexp va a quedar de la forma "<script.</script[^%s]*>", por ej: esto elimina los tags de tipo <script ...>...</script>
		local function clean(tag, text)
			local replacement = ""
			local regexp = "<" .. tag .. ".-</" .. tag .. "[%s]*>"
			text = text:gsub(regexp, replacement)
			--<%s*link.-> esto elimina los tags de tipo <link ... /> o los <link ... > (mal formados pero en fin pueden exisitir)
			regexp = "<%s*" .. tag .. ".->"
			text = text:gsub(regexp, replacement)
			return text
		end
		for index, tag in ipairs({"base", "link", "script", "iframe"}) do text = clean(tag, text) end -- elimino los tags por defecto
		if type(tagList) == "table" then
			for index, tag in ipairs(tagList) do text = clean(string.lower(tag), text) end -- elimino los tags recibidos por parametros
		end
		return text
	end
	--Esta funcion elimina todos los eventos y propiedades en los tags html que puedan ejecutar javascript.
	local function cleanUpJavascriptEventsAndProperties(text)
		--En este for se van eliminando cada uno de los eventos, con cada uno de ellos se va armando una expresion regular case insensitive
		for index, property in ipairs({
				--[[events]] "onabort", "onblur", "onchange", "onclick", "ondblclick", "onerror", "onfocus", "onkeydown", "onkeypress", 
					"onkeyup", "onload", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onreset", "onselect", "onsubmit", "onunload",
				--[[Properties]] {name = "href", replacement = 'href="#1"'}, {name = "target"}}) do
			local event = property
			local replacement = ""
			if type(property) == "table" then
				event = property.name
				replacement = property.replacement or replacement
			end
			--con el evento/metodo genera una expresion regualar para que pueda encontrar case insensitive
			--	ej: el evento "onblur" en la expresion regualr queda "[oO][nN][bB][lL][uU][rR]", entonces la expreg podria encontrar: onblur, ONBLUR, oBluR
			local eventRegExpBase = ""
			for position = 1, string.len(event) do
				local character = string.sub(event, position, position)
				eventRegExpBase = eventRegExpBase .. "[" .. character .. character:upper() .. "]"
			end
			--Al final de la funcion se le contacatena que '%s*=%s*':
			--	puede contener 0 o N espacios en blanco '%s*' luego si o si contiene un '=', después antes del codigo javascript puede contener 0 o N espacios en blanco '%s*'.
			eventRegExpBase = eventRegExpBase .. '%s*=%s*'
			--agrego a la expresion regular para que elimine el codigo javascript que se encuentre entre comillas dobles '".-"'
			local eventRegExp = eventRegExpBase .. '".-"'
			text = string.gsub(text, eventRegExp, replacement)
			--agrego a la expresion regular para que elimine el codigo javascript que se encuentre entre comillas simples "'.-'"
			eventRegExp = eventRegExpBase .. "'.-'"
			text = string.gsub(text, eventRegExp, replacement)
			--agrego a la expresion regular para que elimine el codigo javascript que NO se encuentra entre comillas dobles ni simples y busca / o > como fin de codigo
			eventRegExp = eventRegExpBase .. ".-[%s/>]"
			text = string.gsub(text, eventRegExp, function(match)
				local lastCharacter = match:sub(match:len(), match:len())
				if lastCharacter == " " then -- si el ultimo caractar es un espacio retorna el match
					return replacement
				else -- si el utlimo caracter NO es un espacio retorna el match contactatenado el ultimo caracter. Esto es asi para que no se pierte el / o el >
					return replacement .. " " .. lastCharacter
				end
			end)
		end
		return text
	end
	--Esta funcion elimina del css existente en el html las ejecucion javascript
	local function cleanUpJavascriptCalledFromCss(text)
		local regexp = "[eE][xX][pP][rR][eE][sS][sS][iI][oO][nN]%(.-%)" -- elimina la ejecución "expression" ej: background-color:expression( "yellow" ) ;
		text = text:gsub(regexp, "")
		regexp = "[bB][eE][hH][aA][vV][iI][oO][rR]%s*:.-%)" -- elimina el atributo behaviour y su contendido ej: behavior:	url("css/test.htc");
		text = text:gsub(regexp, "")
		return text
	end
	text = text:gsub("<[^%s>]+", string.lower) --paso todos los tags a minuscula
	text = cleanUpTags(text, tagList) -- elimino el contenido de los tags
	text = cleanUpJavascriptEventsAndProperties(text) -- elimino del contenido todos los eventos y metodos javascirpt
	text = cleanUpJavascriptCalledFromCss(text) -- elimino del contentido de los css de la pagina todo codigo que se pueda ejecutar javascript
	return text
end
