package net.sf.openrocket.simulation.extension.impl; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; import java.util.prefs.BackingStoreException; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.util.ArrayList; import net.sf.openrocket.util.BugException; import com.google.inject.Inject; /** * Utility class used by the scripting extension and its configurator. */ public class ScriptingUtil { static final String NODE_ID = ScriptingExtension.class.getCanonicalName(); private static final List<String> DEFAULT_TRUSTED_HASHES = Arrays.asList( // Roll control script in roll control example file: "SHA-256:9bf364ce4d4a75f09b29178bf9d6872b232084f73dae20dc7b5b073e54e95a42" ); /** The name to be chosen from a list of alternatives. If not found, will use the default name. */ private static final List<String> PREFERRED_LANGUAGE_NAMES = Arrays.asList("JavaScript"); @Inject Preferences prefs; /** * Return the preferred internal language name based on a script language name. * * @return the preferred language name, or null if the language is not supported. */ public String getLanguage(String language) { if (language == null) { return null; } ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName(language); if (engine == null) { return null; } return getLanguage(engine.getFactory()); } public List<String> getLanguages() { List<String> langs = new ArrayList<String>(); ScriptEngineManager manager = new ScriptEngineManager(); for (ScriptEngineFactory factory : manager.getEngineFactories()) { langs.add(getLanguage(factory)); } return langs; } private String getLanguage(ScriptEngineFactory factory) { for (String name : factory.getNames()) { if (PREFERRED_LANGUAGE_NAMES.contains(name)) { return name; } } return factory.getLanguageName(); } /** * Test whether the user has indicated this script to be trusted, * or if it is an internally trusted script. */ public boolean isTrustedScript(String language, String script) { if (language == null || script == null) { return false; } script = normalize(script); if (script.length() == 0) { return true; } String hash = hash(language, script); if (DEFAULT_TRUSTED_HASHES.contains(hash)) { return true; } return prefs.getNode(NODE_ID).getBoolean(hash, false); } /** * Mark a script as trusted. */ public void setTrustedScript(String language, String script, boolean trusted) { script = normalize(script); String hash = hash(language, script); if (trusted) { prefs.getNode(NODE_ID).putBoolean(hash, true); } else { prefs.getNode(NODE_ID).remove(hash); } } /** * Clear all trusted scripts. */ public void clearTrustedScripts() { try { prefs.getNode(NODE_ID).clear(); } catch (BackingStoreException e) { throw new BugException(e); } } static String normalize(String script) { return script.replaceAll("\r", "").trim(); } static String hash(String language, String script) { /* * NOTE: Hash length must be max 80 chars, the max length of a key in a Properties object. */ String output; MessageDigest digest; try { digest = MessageDigest.getInstance("SHA-256"); digest.update(language.getBytes("UTF-8")); digest.update((byte) '|'); byte[] hash = digest.digest(script.getBytes("UTF-8")); BigInteger bigInt = new BigInteger(1, hash); output = bigInt.toString(16); while (output.length() < 64) { output = "0" + output; } } catch (NoSuchAlgorithmException e) { throw new BugException("JRE does not support SHA-256 hash algorithm", e); } catch (UnsupportedEncodingException e) { throw new BugException(e); } return digest.getAlgorithm() + ":" + output; } }