/* * Created on Nov 22, 2006 * *Copyright Reliable Response, 2006 */ package net.reliableresponse.notification.providers; import java.io.DataInputStream; import java.io.IOException; import java.io.PrintStream; import java.util.Enumeration; import java.util.Hashtable; import java.util.TooManyListenersException; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.comm.CommPortIdentifier; import javax.comm.PortInUseException; import javax.comm.SerialPort; import javax.comm.SerialPortEvent; import javax.comm.SerialPortEventListener; import javax.comm.UnsupportedCommOperationException; import com.echomine.common.SendMessageFailedException; import net.reliableresponse.notification.Notification; import net.reliableresponse.notification.NotificationException; import net.reliableresponse.notification.broker.BrokerFactory; import net.reliableresponse.notification.broker.impl.clustered.ClusteredServiceManager; import net.reliableresponse.notification.device.Device; import net.reliableresponse.notification.device.SMSDevice; import net.reliableresponse.notification.usermgmt.UnknownUser; import net.reliableresponse.notification.usermgmt.User; public class SMSNotificationProvider extends AbstractNotificationProvider { private static SMSNotificationProvider instance; public SMSNotificationProvider() { SMSInternalProvider.getInstance(); } public void init(Hashtable params) throws NotificationException { if (!ClusteredServiceManager.getInstance().willRun("SMS")) { return; } SMSInternalProvider.getInstance(); } public Hashtable sendNotification(Notification notification, Device device) throws NotificationException { if (!(device instanceof SMSDevice)) { throw new NotificationException(NotificationException.FAILED, "Supplied device isn't a cell phone"); } if (!ClusteredServiceManager.getInstance().willRun("SMS")) { ClusteredServiceManager.getInstance().sendNotificationToDevice (notification, "SMS", notification.getDisplayText(), device.getUuid()); return new Hashtable(); } StringBuffer text = new StringBuffer(); text.append(notification.getDisplayText()); text.append("\n"); text.append("Reply with: "); String[] responses = notification.getSender().getAvailableResponses( notification); for (int i = 0; i < responses.length; i++) { text.append("\n " + responses[i] + " " + notification.getUuid()); } SMSInternalProvider.getInstance().sendSMS( ((SMSDevice) device).getNormalizedNumber(), text.toString()); return getParameters(notification, device); } public Hashtable getParameters(Notification notification, Device device) { Hashtable hashtable = new Hashtable(); return hashtable; } public String[] getResponses(Notification notification) { SMSInternalProvider.getInstance().getNewMessages(); return new String[0]; } public boolean cancelPage(Notification notification) { return false; } public String getName() { return "SMS"; } } class SMSInternalProvider { private static SMSInternalProvider instance; private SerialPort serialPort; private PrintStream outputWriter; private DataInputStream inputReader; Object serialPortLock; private SMSInternalProvider() { } public static SMSInternalProvider getInstance() { if (instance == null) { instance = new SMSInternalProvider(); instance.init(); } return instance; } public void init() { BrokerFactory.getLoggingBroker().logDebug( "Before init, serial port=" + serialPort); if (serialPort == null) { serialPortLock = new Object(); try { serialPort = openSerialPort(); BrokerFactory.getLoggingBroker().logDebug( "After init, serial port=" + serialPort); } catch (NotificationException e) { BrokerFactory.getLoggingBroker().logError(e); } } } /** * This is run when we get some text on the serial port */ public void getNewMessages() { synchronized (serialPortLock) { BrokerFactory.getLoggingBroker().logDebug("Checking for new SMS messages"); try { // Print out the command to read all unread messages outputWriter.println ("AT+CMGL=\"REC UNREAD\""); outputWriter.flush(); String line = ""; while (line.equals("")) { line = inputReader.readLine(); BrokerFactory.getLoggingBroker().logDebug("Read from SMS modem: "+line); } while (!line.equalsIgnoreCase("OK")) { BrokerFactory.getLoggingBroker().logDebug( "Read from SMS modem: " + line); if (line.toLowerCase().startsWith("+CMGL:")) { String info = line; String response = inputReader.readLine(); BrokerFactory.getLoggingBroker().logDebug( "Response: " + response); // Parse out the return phone number and find the user // with that phone StringTokenizer tok = new StringTokenizer(info, ","); tok.nextElement(); tok.nextElement(); String phoneNum = (String)tok.nextElement(); if (phoneNum != null) { if (phoneNum.startsWith("\"")) { phoneNum = phoneNum.substring(1, phoneNum .length()); } if (phoneNum.endsWith("\"")) { phoneNum = phoneNum.substring(0, phoneNum .length() - 1); } phoneNum = SMSDevice.normalize(phoneNum); BrokerFactory.getLoggingBroker().logDebug( "Return phone # = " + phoneNum); } else { phoneNum = "UNKNOWN"; } User user = new UnknownUser(); User[] users = BrokerFactory .getUserMgmtBroker() .getUsersWithDeviceType( "net.reliableresponse.notification.device.SMSDevice"); BrokerFactory.getLoggingBroker().logDebug( "We found " + users.length + " users with SMS phones"); if ((users != null) && (users.length > 0)) { for (int userNum = 0; userNum < users.length; userNum++) { Device[] devices = users[userNum].getDevices(); for (int deviceNum = 0; deviceNum < devices.length; deviceNum++) { if (devices[deviceNum] instanceof SMSDevice) { SMSDevice smsDevice = (SMSDevice) devices[deviceNum]; if (smsDevice.getNormalizedNumber() .equals(phoneNum)) { user = users[userNum]; } } } } } // Do something with the text Pattern pattern = Pattern.compile("\\b(\\d{7})\\b"); Matcher matcher = pattern.matcher(response); if (matcher.find()) { BrokerFactory.getLoggingBroker().logDebug( "Response=" + response); BrokerFactory.getLoggingBroker().logDebug( "Start=" + matcher.start()); BrokerFactory.getLoggingBroker().logDebug( "End=" + matcher.end()); String uuid = response.substring(matcher.start(), matcher.end()); BrokerFactory.getLoggingBroker().logDebug( "Found uuid " + uuid); Notification notification = BrokerFactory .getNotificationBroker() .getNotificationByUuid(uuid); if (notification == null) { BrokerFactory .getLoggingBroker() .logInfo( "Could not find notification " + uuid + " referenced by SMS message " + response); return; } String[] responses = notification.getSender() .getAvailableResponses(notification); BrokerFactory.getLoggingBroker().logDebug( "We found " + responses.length + " responses"); for (int i = 0; i < responses.length; i++) { BrokerFactory.getLoggingBroker().logDebug( "Checking response " + responses[i]); if (response.toLowerCase().indexOf( responses[i].toLowerCase()) >= 0) { notification.getSender().handleResponse( notification, user, responses[i], response); } } } // Check for commands if (user != null) { String responseToAction = AbstractNotificationProvider .getResponseToAction(user, response); if (responseToAction != null) { try { sendSMS(phoneNum, responseToAction); } catch (Exception e) { BrokerFactory.getLoggingBroker() .logError(e); } } } } line = ""; while (line.equals("")) { line = inputReader.readLine(); } } } catch (IOException e) { BrokerFactory.getLoggingBroker().logError(e); } } } public boolean writeToModem(PrintStream out, DataInputStream in, String output, String expected) throws IOException { BrokerFactory.getLoggingBroker().logDebug("SMS Out: " + output); out.print(output + "\r\n"); out.flush(); try { Thread.sleep(300); } catch (InterruptedException e) { BrokerFactory.getLoggingBroker().logError(e); } String line = ""; boolean found = false; while (!found) { line = in.readLine(); BrokerFactory.getLoggingBroker().logDebug("read: " + line); if (line != null) { if (line.length() > 0) { if (!line.startsWith(output)) { found = line.toLowerCase().indexOf( expected.toLowerCase()) >= 0; return found; } } } } return false; } public void sendSMS(String phoneNumber, String message) throws NotificationException { BrokerFactory.getLoggingBroker().logDebug("Sending SMS message to "+phoneNumber); synchronized (serialPortLock) { if (serialPort == null) { serialPort = openSerialPort(); } try { serialPort.notifyOnDataAvailable(false); if (!writeToModem(outputWriter, inputReader, "ATE0", "OK")) { outputWriter.print((char) 0x1a); if (!writeToModem(outputWriter, inputReader, "ATE0", "OK")) { serialPort = openSerialPort(); if (!writeToModem(outputWriter, inputReader, "ATE0", "OK")) { throw new NotificationException( NotificationException.INTERNAL_ERROR, "Could not open serial port"); } } } // clear out the input reader while (inputReader.available() > 0) { inputReader.read(); } BrokerFactory.getLoggingBroker() .logDebug("Sending SMS message"); outputWriter.println("AT+CMGS=\"" + phoneNumber + "\""); String outputMessage = message + (char) (0x1a); outputWriter.println(message); outputWriter.flush(); // Let that go through before we sent the ctrl-z to finish the // message try { Thread.sleep(300); } catch (InterruptedException e) { } // Flush out any spurious >'s while (inputReader.available() > 0) { inputReader.read(); } outputWriter.print((char) (0x1a)); outputWriter.flush(); String line = ""; int count = 0; while ((line.equals("")) && (count < 5)) { line = inputReader.readLine(); count++; } // if (!line.startsWith("+CMGS")) { // throw new NotificationException(NotificationException.FAILED, // "Bad modem response: "+line); // } } catch (IOException e) { BrokerFactory.getLoggingBroker().logError(e); throw new NotificationException( NotificationException.TEMPORARILY_FAILED, e .getMessage()); } } } /** * @param device * @throws NotificationException */ private SerialPort openSerialPort() throws NotificationException { SerialPort serialPort; String defaultPort = BrokerFactory.getConfigurationBroker() .getStringValue("modem.port", "/dev/ttyS0"); Enumeration portList = CommPortIdentifier.getPortIdentifiers(); while (portList.hasMoreElements()) { CommPortIdentifier portId = (CommPortIdentifier) portList .nextElement(); if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { if (portId.getName().equals(defaultPort)) { BrokerFactory.getLoggingBroker().logDebug( "Using modem on port " + defaultPort + " with 8N1"); try { serialPort = (SerialPort) portId.open( "Reliable Response Notification", 2000); // This is stupid. JavaComm needs to have this // System.out.println in // order to initialize properly System.out.println("setSerialPortParams()"); serialPort.setSerialPortParams(2400, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); outputWriter = new PrintStream(serialPort .getOutputStream()); inputReader = new DataInputStream(serialPort .getInputStream()); writeToModem(outputWriter, inputReader, "ATE0", "OK"); writeToModem(outputWriter, inputReader, "AT+CNMI=2,0,0,0,0", "OK"); return serialPort; } catch (IOException e) { BrokerFactory.getLoggingBroker().logError(e); throw new NotificationException( NotificationException.INTERNAL_ERROR, "Can't get output stream"); } catch (PortInUseException e) { BrokerFactory.getLoggingBroker().logError(e); throw new NotificationException( NotificationException.TEMPORARILY_FAILED, "Modem in use"); } catch (UnsupportedCommOperationException e) { BrokerFactory.getLoggingBroker().logError(e); throw new NotificationException( NotificationException.TEMPORARILY_FAILED, "Temporary modem problem"); } } } } return null; } }