package esmska.transfer; import esmska.data.Keyring; import esmska.data.Gateway; import esmska.data.Gateways; import esmska.data.SMS; import esmska.data.Tuple; import esmska.transfer.GatewayExecutor.Problem; import esmska.utils.L10N; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; /** Class that takes care of parsing gateway script files for extracting * gateway info and sending messages. * * @author ripper */ public class GatewayInterpreter { private static final Logger logger = Logger.getLogger(GatewayInterpreter.class.getName()); private static final ResourceBundle l10n = L10N.l10nBundle; private static final ScriptEngineManager manager = new ScriptEngineManager(); private static final Keyring keyring = Keyring.getInstance(); private Map<GatewayVariable, String> variables; private GatewayExecutor executor; private ScriptEngine engine; private Invocable invocable; /** recreate needed variables before every use */ private void init() { if (engine == null) { engine = manager.getEngineByName("js"); invocable = (Invocable) engine; } if (engine == null) { throw new IllegalStateException("JavaScript execution not supported"); } if (variables == null) { variables = new HashMap<GatewayVariable, String>(); } } /** Send a message * @param sms sms to be sent * @return whether the message was sent successfully */ public boolean sendMessage(SMS sms) throws Exception { Gateway gateway = Gateways.getInstance().get(sms.getGateway()); logger.log(Level.FINE, "Sending SMS to: {0}", gateway); init(); executor = new GatewayExecutor(sms); if (gateway == null) { executor.setProblem(Problem.INTERNAL_MESSAGE, l10n.getString("GatewayInterpreter.unknown_gateway")); return false; } this.variables = extractVariables(sms, gateway); Reader reader = null; boolean sentOk = false; try { reader = new InputStreamReader(gateway.getScript().openStream(), "UTF-8"); //set preferred language String language = getPreferredLanguage(gateway); executor.setPreferredLanguage(language); //forward variables to the script and evaluate it forwardVariables(); engine.eval(reader); //send the message sentOk = (Boolean) invocable.invokeFunction("send", new Object[0]); logger.log(Level.FINE, "SMS sent ok: {0}", sentOk); //if sending failed but no problem provided, set to unknown if (!sentOk && sms.getProblem() == null) { executor.setProblem(Problem.UNKNOWN, null); } } catch (ScriptException ex) { logger.log(Level.SEVERE, "Error executing gateway script file " + gateway, ex); executor.setProblem(Problem.UNKNOWN, null); return false; } finally { try { if (reader != null){ reader.close(); } } catch (IOException ex) { logger.log(Level.SEVERE, "Error closing gateway script file " + gateway, ex); } } return sentOk; } /** Extract variables from SMS to a map */ private static HashMap<GatewayVariable,String> extractVariables(SMS sms, Gateway gateway) { HashMap<GatewayVariable,String> map = new HashMap<GatewayVariable, String>(); map.put(GatewayVariable.NUMBER, sms.getNumber()); map.put(GatewayVariable.MESSAGE, sms.getText()); map.put(GatewayVariable.SENDERNAME, sms.getSenderName()); map.put(GatewayVariable.SENDERNUMBER, sms.getSenderNumber()); Tuple<String, String> key = keyring.getKey(sms.getGateway()); if (key != null) { map.put(GatewayVariable.LOGIN, key.get1()); map.put(GatewayVariable.PASSWORD, key.get2()); } if (gateway.getConfig().isReceipt()) { map.put(GatewayVariable.RECEIPT, "true"); } return map; } /** Forward all the declared variables into the script. * All variable values are transformed to the x-www-form-urlencoded format. */ private void forwardVariables() { for (GatewayVariable var : GatewayVariable.values()) { String value = variables.get(var); engine.put(var.toString(), value != null ? value : ""); } engine.put("EXEC", executor); } /** Compute preffered language to retrieve web content based on user default * language and set of supported languages by gateway script. * @return two-letter language code as defined in ISO 639-1 */ private String getPreferredLanguage(Gateway gateway) { List<String> languages = Arrays.asList(gateway.getSupportedLanguages()); String defLang = Locale.getDefault().getLanguage(); if (languages.isEmpty() || languages.contains(defLang)) { return defLang; } else { return languages.get(0); } } }