/* * FrontlineSMS <http://www.frontlinesms.com> * Copyright 2011 kiwanja * * This file is part of FrontlineSMS. * * FrontlineSMS is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * FrontlineSMS 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 Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with FrontlineSMS. If not, see <http://www.gnu.org/licenses/>. */ package net.frontlinesms.messaging.sms.internet; import java.io.IOException; import java.util.LinkedHashMap; import javax.xml.parsers.ParserConfigurationException; import net.frontlinesms.FrontlineUtils; import net.frontlinesms.data.domain.FrontlineMessage; import net.frontlinesms.data.domain.FrontlineMessage.Status; import net.frontlinesms.messaging.Provider; import net.frontlinesms.messaging.sms.properties.PasswordString; import net.frontlinesms.messaging.sms.properties.PhoneSection; import net.frontlinesms.ui.handler.settings.SmsInternetServiceSettingsHandler; import org.apache.log4j.Logger; import org.smslib.ReceiveNotSupportedException; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import yo.sms.service.HttpConnection; import yo.sms.service.MTService; import yo.sms.service.XmlBuilder; import yo.sms.service.XmlEntityBuilder; /** * Implements Yo! internet SMS service * @author Eric <elwanga@yo.co.ug> * */ @Provider(name = "Yo! (beta)", icon = "/icons/sms_http.png") public class YoInternetService extends AbstractSmsInternetService { /** * Prefix attached to every property name. Used in application internet * service settings. */ protected static final String PROPERTY_PREFIX = "smsdevice.internet.yo."; protected static final String PROPERTY_USERNAME = PROPERTY_PREFIX + "username"; protected static final String PROPERTY_PASSWORD = PROPERTY_PREFIX + "password"; protected static final String PROPERTY_FROM_MSISDN = PROPERTY_PREFIX + "from.msisdn"; protected static final String PROPERTY_SSL = PROPERTY_PREFIX + "ssl"; private static final String YBSSMGW_TAG = "YbsSmgw"; private static final String REQUEST_TAG = "Request"; private static final String METHOD_TAG = "Method"; private static final String ACCOUNT_TAG = "Account"; private static final String USERNAME_TAG = "Username"; private static final String PASSWORD_TAG = "Password"; private static final String STATUS_TAG = "Status"; private static final String ERROR_MESSAGE = "ErrorMessage"; private static final String AUTHENTICITY_TAG = "Authenticity"; private static final String AUTHENTICATE = "/authenticate"; private static final String METHOD = "CheckClientAuthenticity"; /** Logging object */ private static Logger LOG = FrontlineUtils.getLogger(YoInternetService.class); @Override protected void deinit() { this.setStatus(SmsInternetServiceStatus.DISCONNECTED, null); } @Override protected void init() throws SmsInternetServiceInitialisationException { if (verifyCredentials(getUsername(), getUsername(), getPassword(), isEncrypted())) { this.setStatus(SmsInternetServiceStatus.CONNECTED, null); } else { LOG.info("[DEBUG] init: Failed to connect "); this.setStatus(SmsInternetServiceStatus.FAILED_TO_CONNECT, "Invalid username/password or verify connection"); } } @Override protected void receiveSms() throws ReceiveNotSupportedException { throw new ReceiveNotSupportedException(); } @Override protected void sendSmsDirect(FrontlineMessage message) { LOG.debug("Sending [" + message.getTextContent() + "] to [" + message.getRecipientMsisdn() + "]"); MTService mTService = new MTService(); String xmlTextRequest = mTService.buildXmlRequestEntity(getUsername(), getUsername(), getPassword(), message); try { String response = mTService.postXmlRequest(xmlTextRequest, isEncrypted()); String messageStatus = processMTResponse(response); if (messageStatus != null) { if (messageStatus.equals("INSUFFICIENT")) { LOG.info("Insufficient Credit"); this.setStatus(SmsInternetServiceStatus.LOW_CREDIT, ""); message.setStatus(Status.FAILED); } else { LOG.info("Message sent"); message.setStatus(Status.SENT); } } else { LOG.info("Null response"); message.setStatus(Status.FAILED); } } catch (IOException e) { LOG.debug("[DEBUG] Failed to send message: " + e.getMessage()); message.setStatus(Status.FAILED); } finally { if (smsListener != null) { smsListener.outgoingMessageEvent(this, message); } } } private String processMTResponse(String response) { try { Document document = XmlBuilder.parseXml(response); NodeList nodeList = document.getElementsByTagName(STATUS_TAG); if (nodeList.getLength() > 0) { return nodeList.item(0).getTextContent(); } } catch (SAXException e) { LOG.debug("Error: " + e.getMessage()); } catch (IOException e) { LOG.debug("IO Error: " + e.getMessage()); } catch (ParserConfigurationException e) { LOG.debug("Parse Error: " + e.getMessage()); } return null; } public String getIdentifier() { return getPropertyValue(PROPERTY_USERNAME, String.class); } public String getMsisdn() { return getPropertyValue(PROPERTY_FROM_MSISDN, PhoneSection.class).getValue(); } public LinkedHashMap<String, Object> getPropertiesStructure() { LinkedHashMap<String, Object> defaultSettings = new LinkedHashMap<String, Object>(); defaultSettings.put(PROPERTY_USERNAME, ""); defaultSettings.put(PROPERTY_PASSWORD, new PasswordString("")); defaultSettings.put(PROPERTY_FROM_MSISDN, new PhoneSection("")); // defaultSettings.put(PROPERTY_SSL, Boolean.FALSE); defaultSettings.put(PROPERTY_USE_FOR_SENDING, Boolean.TRUE); // defaultSettings.put(PROPERTY_USE_FOR_RECEIVING, Boolean.FALSE); return defaultSettings; } public boolean isConnected() { /* * try { InetAddress host = * InetAddress.getByName(HttpConnection.PRIMARY_GATEWAY_ADDRESS); * this.setStatus(SmsInternetServiceStatus.CONNECTED, null); return * host.isReachable(1000); } catch (UnknownHostException e) { * this.setStatus(SmsInternetServiceStatus.DISCONNECTED, null); return * false; } catch (IOException e) { * this.setStatus(SmsInternetServiceStatus.DISCONNECTED, null); return * false; } */ return true; } public boolean isEncrypted() { // return getPropertyValue(PROPERTY_SSL, Boolean.class); return false; } public boolean isBinarySendingSupported() { return false; } public boolean isUcs2SendingSupported() { return false; } public void setUseForReceiving(boolean use) { this.setProperty(PROPERTY_USE_FOR_RECEIVING, new Boolean(use)); } public void setUseForSending(boolean use) { this.setProperty(PROPERTY_USE_FOR_SENDING, new Boolean(use)); } public boolean supportsReceive() { return true; } public String getDisplayPort() { return null; } public String getServiceName() { return getPropertyValue(PROPERTY_USERNAME, String.class) + UI_NAME_SEPARATOR + SmsInternetServiceSettingsHandler.getProviderName(getClass()); } /** * @return The property value of {@value #PROPERTY_USERNAME} */ private String getUsername() { return getPropertyValue(PROPERTY_USERNAME, String.class); } /** * @return The property value of {@value #PROPERTY_PASSWORD} */ private String getPassword() { return getPropertyValue(PROPERTY_PASSWORD, PasswordString.class).getValue(); } public boolean isUseForReceiving() { return false; // return getPropertyValue(PROPERTY_USE_FOR_RECEIVING, Boolean.class); } public boolean isUseForSending() { return getPropertyValue(PROPERTY_USE_FOR_SENDING, Boolean.class); } private boolean verifyCredentials(String accountNumber, String username, String password, boolean secure) { final int METHOD_TYPE = 2; final int ACCOUNT_NUMBER = 3; final int USERNAME = 4; final int PASSWORD = 5; this.setStatus(SmsInternetServiceStatus.CONNECTING, null); XmlEntityBuilder xmlEntityBuilder = new XmlEntityBuilder(); String[] startTags = { YBSSMGW_TAG, REQUEST_TAG, METHOD_TAG, ACCOUNT_TAG, USERNAME_TAG, PASSWORD_TAG }; for (int i = 0; i < 6; i++) { xmlEntityBuilder.writeStartElement(startTags[i]); switch (i) { case METHOD_TYPE: xmlEntityBuilder.writeText(METHOD); xmlEntityBuilder.writeEndElement(); break; case ACCOUNT_NUMBER:// A/c# or username xmlEntityBuilder.writeText(accountNumber); xmlEntityBuilder.writeEndElement(); break; case PASSWORD: xmlEntityBuilder.writeText(password); xmlEntityBuilder.writeEndElement(); break; case USERNAME: xmlEntityBuilder.writeText(username); xmlEntityBuilder.writeEndElement(); break; } } // Close "YbsSmgw" and "Request" tags xmlEntityBuilder.writeEndElement(); xmlEntityBuilder.writeEndElement(); String authenticationRequest = xmlEntityBuilder.getStringEntity(); try { String response = HttpConnection.postData(authenticationRequest, AUTHENTICATE, secure); Document doc; doc = XmlBuilder.parseXml(response); NodeList nodeList = doc.getElementsByTagName(STATUS_TAG); if (nodeList.item(0).getTextContent().equals("ERROR")) { nodeList = doc.getElementsByTagName(ERROR_MESSAGE); LOG.debug("Authenticate Error: " + nodeList.item(0).getTextContent()); return false; } nodeList = doc.getElementsByTagName(ACCOUNT_TAG); if (nodeList.item(0).getTextContent().equals(accountNumber)) { nodeList = doc.getElementsByTagName(AUTHENTICITY_TAG); if (nodeList.item(0).getTextContent().equals("VALID")) { return true; } } else { return false; } } catch (IOException e) { LOG.debug("IO Error: " + e.getMessage()); return false; } catch (SAXException e) { LOG.debug("Error: " + e.getMessage()); return false; } catch (ParserConfigurationException e) { LOG.debug("Parse Error: " + e.getMessage()); return false; } return false; } }