package esmska.data; import esmska.utils.L10N; import esmska.utils.MiscUtils; import java.beans.IntrospectionException; import java.io.IOException; import java.net.URL; import java.security.PrivilegedActionException; import java.text.Collator; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import javax.script.ScriptException; import javax.swing.Icon; import javax.swing.ImageIcon; import org.apache.commons.lang.StringUtils; /** Class representing a web gateway. * This implementation caches all information retrieved from gateway script * in order to reduce the performance impact caused by javascript invocations. */ public class Gateway implements GatewayInfo, Comparable<Gateway> { /** This enum attributes indicate which various features are supported by a given gateway. */ public static enum Feature { /** The gateway supports logging in with username and password, but also * works without it. */ LOGIN, /** The gateway requires login with username and password. It means the * user must have some credentials assigned from the operator or must * register at gateway website prior to using this gateway. */ LOGIN_ONLY, /** The gateway is able to send a message as if it was coming from the * specified sender's cell number. */ SENDER_NUMBER, /** The gateway will send a delivery report upon request. This usually * means sending an SMS back to the sender as soon as the message reaches * the recipient. */ RECEIPT, /** This gateway requires (always or in some circumstances) transcribing * a security code (captcha). */ CAPTCHA, } public static final String UNKNOWN = L10N.l10nBundle.getString("Gateway.unknown"); private static final Logger logger = Logger.getLogger(Gateway.class.getName()); // maximum number of characters the user can send at once public static final int maxMessageLength = 1000; // maximum multiplier of messages private static final int maxMaxParts = 5; private URL script; private String name, version, maintainer, website, description, minProgramVersion; private String[] supportedPrefixes, preferredPrefixes, supportedLanguages, features; private int smsLength, maxParts, maxChars, delayBetweenMessages; private Icon icon; private boolean favorite, hidden; private GatewayConfig config = new GatewayConfig(); private static final Pattern namePattern = Pattern.compile("^\\[(\\w\\w|" + CountryPrefix.INTERNATIONAL_CODE + ")\\].+"); private static final Collator collator = Collator.getInstance(); /** Creates new Gateway. * * @param script system resource containing gateway script * @throws IOException When there are problem accessing the script file * @throws ScriptException When gateway script is invalid * @throws PrivilegedActionException When gateway script is invalid * @throws IntrospectionException When current JRE does not support JavaScript execution * @throws IllegalArgumentException When gateway name is not valid */ public Gateway(URL script) throws IOException, ScriptException, PrivilegedActionException, IntrospectionException { this.script = script; GatewayInfo info = Gateways.parseInfo(script); //check gateway name is valid name = info != null ? info.getName() : null; if (StringUtils.isEmpty(name)) { throw new ScriptException("Not a valid gateway script", script.toExternalForm(), 0); } if (!namePattern.matcher(name).matches()) { throw new IllegalArgumentException("Gateway name not valid: " + name); } //remember all the values from GatewayInfo interface internally in order //to increase speed (Java code vs JavaScript execution for every method access). version = info.getVersion(); maintainer = info.getMaintainer(); minProgramVersion = info.getMinProgramVersion(); website = info.getWebsite(); description = info.getDescription(); supportedPrefixes = info.getSupportedPrefixes(); preferredPrefixes = info.getPreferredPrefixes(); smsLength = info.getSMSLength(); maxChars = Math.min(info.getMaxChars(), maxMessageLength); delayBetweenMessages = info.getDelayBetweenMessages(); supportedLanguages = info.getSupportedLanguages(); features = info.getFeatures(); //find icon - for "[xx]abc.gateway" look for "[xx]abc.png" String iconName = script.toExternalForm().replaceFirst("\\.gateway$", ".png"); URL iconURL = new URL(iconName); icon = new ImageIcon(iconURL); if (icon.getIconWidth() <= 0) { //non-existing icon, zero-sized image icon = Icons.GATEWAY_DEFAULT; } // compute maxParts maxParts = maxMessageLength / maxChars; maxParts = Math.min(maxParts, maxMaxParts); logger.log(Level.FINER, "Created new gateway: {0}", toString()); } /** URL of gateway script (file or jar URL). */ public URL getScript() { return script; } /** Gateway logo icon. * Should be a 16x16px PNG with transparent background. */ public Icon getIcon() { return icon; } /** Return whether this gateway has been marked as user favorite */ public boolean isFavorite() { return favorite; } /** Set this gateway as user favorite */ public void setFavorite(boolean favorite) { this.favorite = favorite; } /** Return whether this gateway has been marked as hidden from the user interface */ public boolean isHidden() { return hidden; } /** Set this gateway as hidden from user interface */ public void setHidden(boolean hidden) { this.hidden = hidden; } /** Decide whether this particular gateway supports given feature. */ public boolean hasFeature(Feature feature) { for (String featName : features) { if (feature.name().equalsIgnoreCase(featName)) { return true; } } return false; } /** Number of allowed messages which user can send at once. * This is a multiplier of the getMaxChars() number. Some gateways offer only * very short crippled messages (eg. max 60 chars, rest with advertisement). * To alleviate this problem, the user can write a longer text (a multiple * of the allowed messages) and it will be split into separate messages * automatically. * * This number will get computed dynamically. It will be the highest * multiplier of getMaxChars() which doesn't exceed maxMessageLength chars * message length and simmultaneously a maximum multiplier maxMaxParts. */ public int getMaxParts() { return maxParts; } public GatewayConfig getConfig() { return config; } public void setConfig(GatewayConfig config) { this.config = config; } /** Get sender name signature that should be prepended to the message * before it is sent. * @return empty string if user does not want any signature to be added; * otherwise user name signature */ public String getSenderName() { Signature signature = Signatures.getInstance().get(getConfig().getSignature()); if (signature == null || StringUtils.isEmpty(signature.getUserName())) { // user doesn't want to add any name signature return ""; } // user wants to add his name signature String result = signature.getUserName(); // remove accents if required if (Config.getInstance().isRemoveAccents()) { result = MiscUtils.removeAccents(result); } return result; } @Override public int compareTo(Gateway o) { return collator.compare(this.getName(), o.getName()); } @Override public int hashCode() { return this.getName().hashCode(); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Gateway)) { return false; } Gateway o = (Gateway) obj; return this.compareTo(o) == 0; } @Override public String toString() { return name + " [version=" + version + "]"; } @Override public String getName() { return name; } @Override public String getVersion() { return version; } @Override public String getMaintainer() { return maintainer; } @Override public String getMinProgramVersion() { return minProgramVersion; } @Override public String getWebsite() { return website; } @Override public String getDescription() { return description; } @Override public String[] getSupportedPrefixes() { return supportedPrefixes; } @Override public String[] getPreferredPrefixes() { return preferredPrefixes; } @Override public int getSMSLength() { return smsLength; } @Override public int getMaxChars() { return maxChars; } @Override public int getDelayBetweenMessages() { return delayBetweenMessages; } @Override public String[] getSupportedLanguages() { return supportedLanguages; } @Override public String[] getFeatures() { return features; } }