/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2014 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.server.ngclient; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import org.json.JSONObject; import org.jsoup.Jsoup; import org.jsoup.nodes.Attribute; import org.jsoup.nodes.Attributes; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.SecuritySupport; import com.servoy.j2db.util.Settings; import com.servoy.j2db.util.Utils; /** * Convert tags inside html content * @author gboros */ public class HTMLTagsConverter { private static final String[] scanTags = new String[] { "src", "href", "background", "onsubmit", "onreset", "onselect", "onclick", "ondblclick", "onfocus", "onblur", "onchange", "onkeydown", "onkeypress", "onkeyup", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ //$NON-NLS-13$ //$NON-NLS-14$ //$NON-NLS-15$ //$NON-NLS-16$ //$NON-NLS-17$ //$NON-NLS-18$ //$NON-NLS-19$ private static final String javascriptPrefix = "javascript:"; //$NON-NLS-1$ private static final String mediaPrefix = "media://"; //$NON-NLS-1$ private static final String BROWSER_PARAM = "browser:"; //$NON-NLS-1$ public static String convert(String htmlContent, IServoyDataConverterContext context, boolean convertInlineScript) { Document doc = Jsoup.parse(htmlContent); Elements bodyElements = doc.body().getAllElements(); Iterator<Element> bodyElementsIte = bodyElements.iterator(); Element e; Attributes attrs; Iterator<Attribute> attrsIte; Attribute attr; while (bodyElementsIte.hasNext()) { e = bodyElementsIte.next(); attrs = e.attributes(); attrsIte = attrs.iterator(); while (attrsIte.hasNext()) { attr = attrsIte.next(); if (Arrays.asList(scanTags).indexOf(attr.getKey()) != -1) { String replaceContent = attr.getValue(); if (convertInlineScript && replaceContent.startsWith(javascriptPrefix)) { String script = replaceContent.substring(javascriptPrefix.length()); ArrayList<String> browserArguments = getBrowserArguments(script); StringBuffer browserArgumentsMap = new StringBuffer("{"); for (String browserArg : browserArguments) { if (browserArgumentsMap.length() > 1) browserArgumentsMap.append(", "); browserArgumentsMap.append("'").append(browserArg).append("' : ").append(browserArg); } browserArgumentsMap.append("}"); String encryptedFormName = ""; try { encryptedFormName = SecuritySupport.encrypt(Settings.getInstance(), context.getForm().getName()); script = SecuritySupport.encrypt(Settings.getInstance(), script); } catch (Exception ex) { Debug.error("cannot encrypt javascript", ex); script = ""; } attr.setValue(javascriptPrefix + "executeInlineScript('" + encryptedFormName + "', '" + script + "', " + browserArgumentsMap.toString() + ")"); } else if (replaceContent.startsWith(mediaPrefix)) { String media = replaceContent.substring(mediaPrefix.length()); if (media.startsWith("/servoy_blobloader?")) { String blobpart = media.substring("servoy_blobloader?".length()); try { blobpart = SecuritySupport.encryptUrlSafe(Settings.getInstance(), blobpart); attr.setValue("resources/servoy_blobloader?blob=" + blobpart + "&uuid=" + context.getApplication().getWebsocketSession().getUuid()); } catch (Exception e1) { Debug.error("could not encrypt blobloaderpart: " + blobpart); } } else attr.setValue("resources/fs/" + context.getSolution().getName() + media); } } } } return doc.html(); } private static ArrayList<String> getBrowserArguments(String s) { ArrayList<String> browserArguments = new ArrayList<String>(); String escapedScriptName = Utils.stringReplace(Utils.stringReplace(s, "\'", "\\\'"), "\"", """); int browserVariableIndex = escapedScriptName.indexOf(BROWSER_PARAM); if (browserVariableIndex != -1) { while (browserVariableIndex != -1) { // is there a next variable int index = searchEndVariable(escapedScriptName, browserVariableIndex + 8); if (index == -1) { Debug.error("illegal script name encountered with browser arguments: " + escapedScriptName); break; } else { browserArguments.add(escapedScriptName.substring(browserVariableIndex + 8, index)); browserVariableIndex = escapedScriptName.indexOf(BROWSER_PARAM, index); } } } return browserArguments; } private static int searchEndVariable(String script, int start) { int counter = start; int brace = 0; while (counter < script.length()) { switch (script.charAt(counter)) { case '\\' : if (brace == 0) return counter; break; case '&' : if (brace == 0) return counter; break; case '\'' : if (brace == 0) return counter; break; case ',' : if (brace == 0) return counter; break; case '(' : brace++; break; case ')' : if (brace == 0) return counter; brace--; break; } counter++; } return 0; } public static String decryptInlineScript(String encryptedJavascript, JSONObject params) { try { String javascript = SecuritySupport.decrypt(Settings.getInstance(), encryptedJavascript); String browserParamWithArgument; Object arg; for (String browserArgument : getBrowserArguments(javascript)) { browserParamWithArgument = BROWSER_PARAM + browserArgument; arg = params.opt(browserArgument); if (arg instanceof String) arg = "'" + arg + "'"; javascript = javascript.replace(browserParamWithArgument, arg.toString()); } return javascript; } catch (Exception ex) { Debug.error("Cannot decrypt inline javascript", ex); } return null; } }