Package org.smslib.modem

Source Code of org.smslib.modem.ModemGateway

// SMSLib for Java v3
// A Java API library for sending and receiving SMS via a GSM modem
// or other supported gateways.
// Web Site: http://www.smslib.org
//
// Copyright (C) 2002-2012, Thanasis Delenikas, Athens/GREECE.
// SMSLib is distributed under the terms of the Apache License version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.smslib.modem;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.ajwcc.pduUtils.gsm3040.Pdu;
import org.ajwcc.pduUtils.gsm3040.PduParser;
import org.ajwcc.pduUtils.gsm3040.PduUtils;
import org.ajwcc.pduUtils.gsm3040.SmsDeliveryPdu;
import org.ajwcc.pduUtils.gsm3040.SmsStatusReportPdu;
import org.smslib.AGateway;
import org.smslib.Contact;
import org.smslib.GatewayException;
import org.smslib.InboundBinaryMessage;
import org.smslib.InboundEncryptedMessage;
import org.smslib.InboundMessage;
import org.smslib.OutboundMessage;
import org.smslib.Phonebook;
import org.smslib.Service;
import org.smslib.StatusReportMessage;
import org.smslib.TimeoutException;
import org.smslib.USSDRequest;
import org.smslib.UnknownMessage;
import org.smslib.InboundMessage.MessageClasses;
import org.smslib.OutboundMessage.FailureCauses;
import org.smslib.OutboundMessage.MessageStatuses;
import org.smslib.helper.Logger;
import org.smslib.modem.athandler.AATHandler;

/**
* Class representing GSM modems or phones. Extends AGateway with modem specific
* operations.
*/
public class ModemGateway extends AGateway
{
  /**
   * Class representing different types of GSM modems / phones.
   */
  public enum ModemTypes
  {
    /**
     * Serially connected modem. These modems are connected via a serial
     * port, either physical or emulated (i.e. USB, IrDA, etc).
     */
    SERIAL,
    /**
     * IP connected modem.
     */
    IP
  }

  public enum IPProtocols
  {
    TEXT, BINARY
  }

  private AModemDriver driver;

  private AATHandler atHandler;

  private String modemDevice;

  private int modemParms;

  private IPProtocols ipProtocol;

  private boolean ipEncryption;

  private long lastKeepLinkOpen;

  private String manufacturer;

  private String model;

  private String simPin, simPin2;

  private String customInitString;

  private String smscNumber;

  private int outMpRefNo;

  private List<List<InboundMessage>> mpMsgList;

  public ModemGateway(ModemTypes myType, String id, String myModemDevice, int myModemParms, String myManufacturer, String myModel)
  {
    super(id);
    init(myType, myModemDevice, myModemParms, myManufacturer, myModel, null);
  }

  public ModemGateway(String id, String myModemDevice, int myModemParms, String myManufacturer, String myModel, AModemDriver myDriver)
  {
    super(id);
    init(null, myModemDevice, myModemParms, myManufacturer, myModel, myDriver);
  }

  private void init(ModemTypes myType, String myModemDevice, int myModemParms, String myManufacturer, String myModel, AModemDriver myDriver)
  {
    setModemDevice(myModemDevice);
    setModemParms(myModemParms);
    setIpProtocol(IPProtocols.BINARY);
    setIpEncryption(false);
    setLastKeepLinkOpen(-1);
    this.manufacturer = myManufacturer;
    this.model = myModel;
    setAttributes(AGateway.GatewayAttributes.SEND | AGateway.GatewayAttributes.RECEIVE | AGateway.GatewayAttributes.BIGMESSAGES | AGateway.GatewayAttributes.WAPSI | AGateway.GatewayAttributes.PORTADDRESSING | AGateway.GatewayAttributes.FLASHSMS | AGateway.GatewayAttributes.DELIVERYREPORTS);
    if (myDriver != null) setDriver(myDriver);
    else
    {
      if (myType == ModemTypes.SERIAL) setDriver(new SerialModemDriver(this, getModemDevice() + ":" + getModemParms()));
      else setDriver(new IPModemDriver(this, getModemDevice() + ":" + getModemParms()));
    }
    setAtHandler(AATHandler.load(this, this.manufacturer, this.model));
    setSimPin("");
    setSimPin2("");
    setSmscNumber("");
    setCustomInitString("");
    this.outMpRefNo = new Random().nextInt();
    if (this.outMpRefNo < 0) this.outMpRefNo *= -1;
    this.outMpRefNo %= 65536;
    this.mpMsgList = new ArrayList<List<InboundMessage>>();
  }

  public void setIpProtocol(IPProtocols myIpProtocol)
  {
    this.ipProtocol = myIpProtocol;
  }

  public IPProtocols getIpProtocol()
  {
    return this.ipProtocol;
  }

  /**
   * Set the IP modem transmission encryption status
   *
   * @param ipEncryption
   *            the status of encryption
   */
  public void setIpEncryption(boolean ipEncryption)
  {
    this.ipEncryption = ipEncryption;
  }

  /**
   * Check if the encryption of IP modem transmission is enabled
   *
   * @return true if IP encryption is enabled
   */
  public boolean getIpEncryption()
  {
    return this.ipEncryption;
  }

  @Override
  public void startGateway() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    Logger.getInstance().logInfo("Starting gateway, using " + getATHandler().getDescription() + " AT Handler.", null, getGatewayId());
    getDriver().connect();
    Logger.getInstance().logInfo("Signal level/bit error rate: " + getATHandler().getSignalLevel(), null, getGatewayId());
    Logger.getInstance().logInfo("Network registration: " + getATHandler().getNetworkRegistration(), null, getGatewayId());
    Logger.getInstance().logInfo("Network operator: " + getATHandler().getNetworkOperator(), null, getGatewayId());
    super.startGateway();
    Logger.getInstance().logInfo("Gateway started.", null, getGatewayId());
  }

  @Override
  public void stopGateway() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    Logger.getInstance().logInfo("Stopping gateway...", null, getGatewayId());
    getATHandler().done();
    super.stopGateway();
    getDriver().disconnect();
    Logger.getInstance().logInfo("Gateway stopped.", null, getGatewayId());
  }

  @Override
  public void readMessages(Collection<InboundMessage> msgList, MessageClasses msgClass) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getStatus() != GatewayStatuses.STARTED) return;
    synchronized (getDriver().getSYNCCommander())
    {
      if (getProtocol() == Protocols.PDU) readMessagesPDU(msgList, msgClass, 0);
      else if (getProtocol() == Protocols.TEXT) readMessagesTEXT(msgList, msgClass, 0);
    }
  }

  @Override
  public InboundMessage readMessage(String memLoc, int memIndex) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    Collection<InboundMessage> msgList;
    if (getStatus() != GatewayStatuses.STARTED) return null;
    synchronized (getDriver().getSYNCCommander())
    {
      msgList = new ArrayList<InboundMessage>();
      readMessages(msgList, MessageClasses.ALL);
      for (InboundMessage msg : msgList)
        if ((msg.getMemIndex() == memIndex) && (msg.getMemLocation().equals(memLoc))) return msg;
      return null;
    }
  }

  @Override
  public boolean sendMessage(OutboundMessage msg) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    boolean sendKeepLinkOpen = false;
    if (getStatus() != GatewayStatuses.STARTED) return false;
    if (getLastKeepLinkOpen() == -1) sendKeepLinkOpen = true;
    if (!sendKeepLinkOpen)
    {
      if ((System.currentTimeMillis() - getLastKeepLinkOpen()) > 4000) sendKeepLinkOpen = true;
    }
    synchronized (getDriver().getSYNCCommander())
    {
      if (sendKeepLinkOpen) getAtHandler().keepLinkOpen();
      setLastKeepLinkOpen(System.currentTimeMillis());
      if (getProtocol() == Protocols.PDU) return sendMessagePDU(msg);
      else if (getProtocol() == Protocols.TEXT) return sendMessageTEXT(msg);
      else return false;
    }
  }

  @Override
  public boolean deleteMessage(InboundMessage msg) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getStatus() != GatewayStatuses.STARTED) return false;
    synchronized (getDriver().getSYNCCommander())
    {
      if (msg.getMemIndex() >= 0) return deleteMessage(msg.getMemIndex(), msg.getMemLocation());
      else if ((msg.getMemIndex() == -1) && (msg.getMpMemIndex().length() != 0))
      {
        StringTokenizer tokens = new StringTokenizer(msg.getMpMemIndex(), ",");
        while (tokens.hasMoreTokens())
          deleteMessage(Integer.parseInt(tokens.nextToken()), msg.getMemLocation());
      }
      return true;
    }
  }

  @Override
  public int readPhonebook(Phonebook phonebook) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    int count = 0;
    String locations, location, entries, entry;
    locations = getATHandler().readPhonebookLocations();
    if (locations.length() > 0)
    {
      StringTokenizer tokens = new StringTokenizer(locations, ",");
      while (tokens.hasMoreTokens())
      {
        location = tokens.nextToken().replaceAll("\"", "");
        entries = getATHandler().readPhonebook(location);
        if ((entries == null) || (entries.trim().length() == 0)) continue;
        BufferedReader reader = new BufferedReader(new StringReader(entries));
        entry = reader.readLine().trim();
        while (!entry.equalsIgnoreCase("OK"))
        {
          entry = entry.replaceAll("\\s*\\+CPBR:\\s*", "");
          entry = entry.replaceAll("\"\"", "\" \"");
          entry = entry.replaceAll("\"", "");
          StringTokenizer tokens4 = new StringTokenizer(entry, ",");
          String index = tokens4.nextToken();
          index = (index == null ? "" : index.trim());
          String phone = tokens4.nextToken();
          phone = (phone == null ? "" : phone.trim());
          // TODO: Parse number type.
          String type = tokens4.nextToken();
          type = (type == null ? "" : type.trim());
          // TODO: *end*
          String name = tokens4.nextToken();
          name = (name == null ? "" : name.trim());
          phonebook.getContacts().add(new Contact(name, phone, location, Integer.parseInt(index)));
          count++;
          entry = reader.readLine().trim();
        }
        reader.close();
      }
    }
    return count;
  }

  private boolean deleteMessage(int memIndex, String memLocation) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    return getATHandler().deleteMessage(memIndex, memLocation);
  }

  private boolean sendMessageTEXT(OutboundMessage msg) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    int refNo;
    boolean ok = false;
    refNo = getATHandler().sendMessage(0, "", msg.getRecipient(), msg.getText());
    if (refNo >= 0)
    {
      msg.setGatewayId(getGatewayId());
      msg.setRefNo("" + refNo);
      msg.setDispatchDate(new Date());
      msg.setMessageStatus(MessageStatuses.SENT);
      incOutboundMessageCount();
      ok = true;
    }
    else
    {
      msg.setRefNo(null);
      msg.setDispatchDate(null);
      msg.setMessageStatus(MessageStatuses.FAILED);
      msg.setErrorMessage(driver.getLastErrorText());
    }
    return ok;
  }

  private void readMessagesTEXT(Collection<InboundMessage> msgList, MessageClasses msgClass, int myLimit) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    int i, j, memIndex;
    int limit = (myLimit < 0 ? 0 : myLimit);
    String response, line, tmpLine, msgText, originator, dateStr, refNo;
    BufferedReader reader;
    StringTokenizer tokens;
    InboundMessage msg;
    Calendar cal1 = Calendar.getInstance();
    Calendar cal2 = Calendar.getInstance();
    for (int ml = 0; ml < (getATHandler().getStorageLocations().length() / 2); ml++)
    {
      if (getATHandler().switchStorageLocation(getATHandler().getStorageLocations().substring((ml * 2), (ml * 2) + 2)))
      {
        response = getATHandler().listMessages(msgClass);
        response = response.replaceAll("\\s+OK\\s+", "\nOK");
        reader = new BufferedReader(new StringReader(response));
        for (;;)
        {
          line = reader.readLine();
          if (line == null) break;
          line = line.trim();
          if (line.length() > 0) break;
        }
        while (true)
        {
          if (line == null) break;
          if (line.length() <= 0 || line.equalsIgnoreCase("OK")) break;
          i = line.indexOf(':');
          j = line.indexOf(',');
          memIndex = 0;
          try
          {
            memIndex = Integer.parseInt(line.substring(i + 1, j).trim());
          }
          catch (NumberFormatException e)
          {
            // TODO: What to do here?
            Logger.getInstance().logWarn("Incorrect Memory Index number parsed!", e, getGatewayId());
          }
          tokens = new StringTokenizer(line, ",");
          tokens.nextToken();
          tokens.nextToken();
          tmpLine = "";
          if (Character.isDigit(tokens.nextToken().trim().charAt(0)))
          {
            line = line.replaceAll(",,", ", ,");
            tokens = new StringTokenizer(line, ",");
            tokens.nextToken();
            tokens.nextToken();
            tokens.nextToken();
            refNo = tokens.nextToken();
            tokens.nextToken();
            dateStr = tokens.nextToken().replaceAll("\"", "");
            cal1.set(Calendar.YEAR, 2000 + Integer.parseInt(dateStr.substring(0, 2)));
            cal1.set(Calendar.MONTH, Integer.parseInt(dateStr.substring(3, 5)) - 1);
            cal1.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStr.substring(6, 8)));
            dateStr = tokens.nextToken().replaceAll("\"", "");
            cal1.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateStr.substring(0, 2)));
            cal1.set(Calendar.MINUTE, Integer.parseInt(dateStr.substring(3, 5)));
            cal1.set(Calendar.SECOND, Integer.parseInt(dateStr.substring(6, 8)));
            dateStr = tokens.nextToken().replaceAll("\"", "");
            cal2.set(Calendar.YEAR, 2000 + Integer.parseInt(dateStr.substring(0, 2)));
            cal2.set(Calendar.MONTH, Integer.parseInt(dateStr.substring(3, 5)) - 1);
            cal2.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStr.substring(6, 8)));
            dateStr = tokens.nextToken().replaceAll("\"", "");
            cal2.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateStr.substring(0, 2)));
            cal2.set(Calendar.MINUTE, Integer.parseInt(dateStr.substring(3, 5)));
            cal2.set(Calendar.SECOND, Integer.parseInt(dateStr.substring(6, 8)));
            msg = new StatusReportMessage(refNo, memIndex, getATHandler().getStorageLocations().substring((ml * 2), (ml * 2) + 2), cal1.getTime(), cal2.getTime());
            msg.setGatewayId(getGatewayId());
            Logger.getInstance().logDebug("IN-DTLS: MI:" + msg.getMemIndex(), null, getGatewayId());
            msgList.add(msg);
            incInboundMessageCount();
          }
          else
          {
            line = line.replaceAll(",,", ", ,");
            tokens = new StringTokenizer(line, ",");
            tokens.nextToken();
            tokens.nextToken();
            originator = tokens.nextToken().replaceAll("\"", "");
            tokens.nextToken();
            dateStr = tokens.nextToken().replaceAll("\"", "");
            cal1.set(Calendar.YEAR, 2000 + Integer.parseInt(dateStr.substring(0, 2)));
            cal1.set(Calendar.MONTH, Integer.parseInt(dateStr.substring(3, 5)) - 1);
            cal1.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStr.substring(6, 8)));
            dateStr = tokens.nextToken().replaceAll("\"", "");
            cal1.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateStr.substring(0, 2)));
            cal1.set(Calendar.MINUTE, Integer.parseInt(dateStr.substring(3, 5)));
            cal1.set(Calendar.SECOND, Integer.parseInt(dateStr.substring(6, 8)));
            msgText = "";
            while (true)
            {
              tmpLine = reader.readLine();
              if (tmpLine == null) break;
              if (tmpLine.startsWith("+CMGL")) break;
              if (tmpLine.startsWith("+CMGR")) break;
              msgText += (msgText.length() == 0 ? "" : "\n") + tmpLine;
            }
            msgText = msgText.trim();
            msg = new InboundMessage(cal1.getTime(), originator, msgText, memIndex, getATHandler().getStorageLocations().substring((ml * 2), (ml * 2) + 2));
            msg.setGatewayId(getGatewayId());
            Logger.getInstance().logDebug("IN-DTLS: MI:" + msg.getMemIndex(), null, getGatewayId());
            msgList.add(msg);
            incInboundMessageCount();
          }
          while (true)
          {
            //line = reader.readLine();
            line = ((tmpLine == null || tmpLine.length() == 0) ? reader.readLine() : tmpLine);
            if (line == null) break;
            line = line.trim();
            if (line.length() > 0) break;
          }
          if ((limit > 0) && (msgList.size() == limit)) break;
        }
        reader.close();
      }
    }
  }

  private boolean sendMessagePDU(OutboundMessage msg) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    boolean ok = false;
    List<String> pdus = msg.getPdus(getSmscNumber(), this.outMpRefNo);
    for (String pdu : pdus)
    {
      Pdu newPdu = new PduParser().parsePdu(pdu);
      Logger.getInstance().logDebug(newPdu.toString(), null, getGatewayId());
      int j = pdu.length() / 2;
      if (getSmscNumber() == null)
      {
        // Do nothing on purpose!
      }
      else if (getSmscNumber().length() == 0) j--;
      else
      {
        int smscNumberLen = getSmscNumber().length();
        if (getSmscNumber().charAt(0) == '+') smscNumberLen--;
        if (smscNumberLen % 2 != 0) smscNumberLen++;
        int smscLen = (2 + smscNumberLen) / 2;
        j = j - smscLen - 1;
      }
      Logger.getInstance().logDebug("Sending Pdu: " + pdu, null, getGatewayId());
      int refNo = getATHandler().sendMessage(j, pdu, null, null);
      if (refNo >= 0)
      {
        msg.setGatewayId(getGatewayId());
        msg.setRefNo(String.valueOf(refNo));
        msg.setDispatchDate(new Date());
        msg.setMessageStatus(MessageStatuses.SENT);
        msg.setFailureCause(FailureCauses.NO_ERROR);
        incOutboundMessageCount();
        ok = true;
      }
      else
      {
        msg.setRefNo(null);
        msg.setDispatchDate(null);
        msg.setMessageStatus(MessageStatuses.FAILED);
        msg.setFailureCause(FailureCauses.UNKNOWN);
        ok = false;
        msg.setErrorMessage(driver.getLastErrorText());
        break;
      }
      if (!ok) break;
    }
    if (pdus.size() > 1)
    {
      this.outMpRefNo = (this.outMpRefNo + 1) % 65536;
    }
    return ok;
  }

  private void readMessagesPDU(Collection<InboundMessage> msgList, MessageClasses messageClass, int myLimit) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    int i, j, memIndex;
    String response, line, pduString;
    BufferedReader reader;
    int limit = (myLimit < 0 ? 0 : myLimit);
    for (int ml = 0; ml < (getATHandler().getStorageLocations().length() / 2); ml++)
    {
      if (getATHandler().switchStorageLocation(getATHandler().getStorageLocations().substring((ml * 2), (ml * 2) + 2)))
      {
        response = getATHandler().listMessages(messageClass);
        response = response.replaceAll("\\s+OK\\s+", "\nOK");
        reader = new BufferedReader(new StringReader(response));
        for (;;)
        {
          line = reader.readLine();
          if (line == null) break;
          line = line.trim();
          if (line.length() > 0) break;
        }
        // use the parser to determine the message type
        PduParser parser = new PduParser();
        while (true)
        {
          if (line == null) break;
          line = line.trim();
          if (line.length() <= 0 || line.equalsIgnoreCase("OK")) break;
          if (line.length() <= 0 || line.equalsIgnoreCase("ERROR")) break;
          i = line.indexOf(':');
          j = line.indexOf(',');
          memIndex = 0;
          try
          {
            memIndex = Integer.parseInt(line.substring(i + 1, j).trim());
          }
          catch (NumberFormatException e)
          {
            // TODO: What to do here?
            Logger.getInstance().logWarn("Incorrect Memory Index number parsed!", e, getGatewayId());
          }
          // Start modifications by Wim Stevens
          // A line contains something like +CMGL: 1,1,,25
          // first parameter is the memory index
          // last parameter is the length of the PDU, not counting the addressing part
          // the parser must always have an addressing part -> we will add it if required
          i = line.lastIndexOf(',');
          j = line.length();
          int pduSize = 0;
          try
          {
            pduSize = Integer.parseInt(line.substring(i + 1, j).trim());
          }
          catch (NumberFormatException e)
          {
            // TODO: What to do here?
            Logger.getInstance().logWarn("Incorrect pdu size parsed!", e, getGatewayId());
          }
          pduString = reader.readLine().trim();
          if ((pduSize > 0) && ((pduSize * 2) == pduString.length()))
          {
            pduString = "00" + pduString;
          }
          try
          {
            Logger.getInstance().logDebug("READ PDU: " + pduString, null, getGatewayId());
            // this will throw an exception for PDUs
            // it can't classify
            Pdu pdu = parser.parsePdu(pduString);
            // NOTE: maybe a message validity vs the current
            //       date should be put here.
            //       if the message is invalid, the message should
            //       be ignored and but logged
            if (pdu instanceof SmsDeliveryPdu)
            {
              Logger.getInstance().logDebug(pdu.toString(), null, getGatewayId());
              InboundMessage msg;
              String memLocation = getATHandler().getStorageLocations().substring((ml * 2), (ml * 2) + 2);
              if (pdu.isBinary())
              {
                msg = new InboundBinaryMessage((SmsDeliveryPdu) pdu, memIndex, memLocation);
                if (Service.getInstance().getKeyManager().getKey(msg.getOriginator()) != null) msg = new InboundEncryptedMessage((SmsDeliveryPdu) pdu, memIndex, memLocation);
              }
              else
              {
                msg = new InboundMessage((SmsDeliveryPdu) pdu, memIndex, memLocation);
              }
              msg.setGatewayId(getGatewayId());
              Logger.getInstance().logDebug("IN-DTLS: MI:" + msg.getMemIndex() + " REF:" + msg.getMpRefNo() + " MAX:" + msg.getMpMaxNo() + " SEQ:" + msg.getMpSeqNo(), null, getGatewayId());
              if (msg.getMpRefNo() == 0)
              {
                // single message
                msgList.add(msg);
                incInboundMessageCount();
              }
              else
              {
                // multi-part message
                int k, l;
                List<InboundMessage> tmpList;
                InboundMessage listMsg;
                boolean found, duplicate;
                found = false;
                for (k = 0; k < this.mpMsgList.size(); k++)
                {
                  // List of List<InboundMessage>
                  tmpList = this.mpMsgList.get(k);
                  listMsg = tmpList.get(0);
                  // check if current message list is for this message
                  if (listMsg.getMpRefNo() == msg.getMpRefNo())
                  {
                    duplicate = false;
                    // check if the message is already in the message list
                    for (l = 0; l < tmpList.size(); l++)
                    {
                      listMsg = tmpList.get(l);
                      if (listMsg.getMpSeqNo() == msg.getMpSeqNo())
                      {
                        duplicate = true;
                        break;
                      }
                    }
                    if (!duplicate) tmpList.add(msg);
                    found = true;
                    break;
                  }
                }
                if (!found)
                {
                  // no existing list present for this message
                  // add one
                  tmpList = new ArrayList<InboundMessage>();
                  tmpList.add(msg);
                  this.mpMsgList.add(tmpList);
                }
              }
            }
            else if (pdu instanceof SmsStatusReportPdu)
            {
              StatusReportMessage msg;
              msg = new StatusReportMessage((SmsStatusReportPdu) pdu, memIndex, getATHandler().getStorageLocations().substring((ml * 2), (ml * 2) + 2));
              msg.setGatewayId(getGatewayId());
              msgList.add(msg);
              incInboundMessageCount();
            }
            else
            {
              // this theoretically will never happen, but it occasionally does with phones
              // like some Sony Ericssons (e.g. Z610i, SENT messages are included in this list)
              // instead of throwing a RuntimeException, just ignore any messages that are not of type
              // SmsDeliveryPdu
              // SmsStatusReportPdu
              if (this.displayIllegalReceivedMessages)
              {
                Logger.getInstance().logError("Wrong type of PDU detected: " + pdu.getClass().getName(), null, getGatewayId());
                Logger.getInstance().logError("ERROR PDU: " + pduString, null, getGatewayId());
              }
            }
          }
          catch (Exception e)
          {
            // PduFactory will give an exception
            // for PDUs it can't understand
            UnknownMessage msg;
            msg = new UnknownMessage(pduString, memIndex, getATHandler().getStorageLocations().substring((ml * 2), (ml * 2) + 2));
            msg.setGatewayId(getGatewayId());
            msgList.add(msg);
            incInboundMessageCount();
            Logger.getInstance().logError("Unhandled SMS in inbox, skipping...", e, getGatewayId());
            Logger.getInstance().logError("ERROR PDU: " + pduString, null, getGatewayId());
          }
          while (true)
          {
            line = reader.readLine();
            if (line == null) break;
            line = line.trim();
            if (line.length() > 0) break;
          }
          if ((limit > 0) && (msgList.size() == limit)) break;
        }
        reader.close();
      }
    }
    checkMpMsgList(msgList);
    List<InboundMessage> tmpList;
    for (int k = 0; k < this.mpMsgList.size(); k++)
    {
      tmpList = this.mpMsgList.get(k);
      tmpList.clear();
    }
    mpMsgList.clear();
  }

  private boolean displayIllegalReceivedMessages;

  public void setDisplayIllegalReceivedMessages(boolean b)
  {
    this.displayIllegalReceivedMessages = b;
  }

  public String getMessageByIndex(int msgIndex) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    synchronized (getDriver().getSYNCCommander())
    {
      return getATHandler().getMessageByIndex(msgIndex);
    }
  }

  private void checkMpMsgList(Collection<InboundMessage> msgList) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    int k, l, m;
    List<InboundMessage> tmpList;
    InboundMessage listMsg, mpMsg;
    boolean found;
    mpMsg = null;
    Logger.getInstance().logDebug("CheckMpMsgList(): MAINLIST: " + this.mpMsgList.size(), null, getGatewayId());
    for (k = 0; k < this.mpMsgList.size(); k++)
    {
      tmpList = this.mpMsgList.get(k);
      Logger.getInstance().logDebug("CheckMpMsgList(): SUBLIST[" + k + "]: " + tmpList.size(), null, getGatewayId());
      listMsg = tmpList.get(0);
      found = false;
      if (listMsg.getMpMaxNo() == tmpList.size())
      {
        found = true;
        for (l = 0; l < tmpList.size(); l++)
          for (m = 0; m < tmpList.size(); m++)
          {
            listMsg = tmpList.get(m);
            if (listMsg.getMpSeqNo() == (l + 1))
            {
              if (listMsg.getMpSeqNo() == 1)
              {
                mpMsg = listMsg;
                mpMsg.setMpMemIndex(mpMsg.getMemIndex());
                if (listMsg.getMpMaxNo() == 1)
                {
                  msgList.add(mpMsg);
                  incInboundMessageCount();
                }
              }
              else
              {
                if (mpMsg != null)
                {
                  if (mpMsg instanceof InboundBinaryMessage)
                  {
                    InboundBinaryMessage mpMsgBinary = (InboundBinaryMessage) mpMsg;
                    InboundBinaryMessage listMsgBinary = (InboundBinaryMessage) listMsg;
                    mpMsgBinary.addDataBytes(listMsgBinary.getDataBytes());
                  }
                  else
                  {
                    // NEW
                    String textToAdd = listMsg.getText();
                    if (mpMsg.getEndsWithMultiChar())
                    {
                      // adjust first char of textToAdd
                      Logger.getInstance().logDebug("Adjusting dangling multi-char: " + textToAdd.charAt(0) + " --> " + PduUtils.getMultiCharFor(textToAdd.charAt(0)), null, getGatewayId());
                      textToAdd = PduUtils.getMultiCharFor(textToAdd.charAt(0)) + textToAdd.substring(1);
                    }
                    mpMsg.setEndsWithMultiChar(listMsg.getEndsWithMultiChar());
                    try
                    {
                      mpMsg.addText(textToAdd);
                    }
                    catch (UnsupportedEncodingException e)
                    {
                      // TODO: What to do with this?
                    }
                  }
                  mpMsg.setMpSeqNo(listMsg.getMpSeqNo());
                  mpMsg.setMpMemIndex(listMsg.getMemIndex());
                  if (listMsg.getMpSeqNo() == listMsg.getMpMaxNo())
                  {
                    mpMsg.setMemIndex(-1);
                    msgList.add(mpMsg);
                    incInboundMessageCount();
                    mpMsg = null;
                  }
                }
              }
              break;
            }
          }
        tmpList.clear();
        tmpList = null;
      }
      if (found)
      {
        this.mpMsgList.remove(k);
        k--;
      }
    }
    // Check the remaining parts for "orphaned" status
    for (List<InboundMessage> remainingList : this.mpMsgList)
    {
      for (InboundMessage msg : remainingList)
      {
        if (getAgeInHours(msg.getDate()) > Service.getInstance().getSettings().HOURS_TO_ORPHAN) if (Service.getInstance().getOrphanedMessageNotification() != null) if (Service.getInstance().getOrphanedMessageNotification().process(Service.getInstance().getGateway(msg.getGatewayId()), msg) == true) deleteMessage(msg);
      }
    }
  }

  private long getLastKeepLinkOpen()
  {
    return this.lastKeepLinkOpen;
  }

  private void setLastKeepLinkOpen(long lastKeepLinkOpen)
  {
    this.lastKeepLinkOpen = lastKeepLinkOpen;
  }

  /**
   * Sets the SIM PIN.
   *
   * @param mySimPin
   *            The SIM PIN.
   */
  public void setSimPin(String mySimPin)
  {
    this.simPin = mySimPin;
  }

  /**
   * Sets the SIM PIN 2.
   *
   * @param mySimPin2
   *            The SIM PIN 2.
   */
  public void setSimPin2(String mySimPin2)
  {
    this.simPin2 = mySimPin2;
  }

  /**
   * Returns the SIM PIN.
   *
   * @return The SIM PIN.
   */
  public String getSimPin()
  {
    return this.simPin;
  }

  /**
   * Returns the SIM PIN 2.
   *
   * @return The SIM PIN 2.
   */
  public String getSimPin2()
  {
    return this.simPin2;
  }

  public AModemDriver getModemDriver()
  {
    return this.driver;
  }

  public AATHandler getATHandler()
  {
    return this.atHandler;
  }

  /**
   * Returns the Manufacturer string of the modem or phone.
   *
   * @return The Manufacturer string.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public String getManufacturer() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    String response;
    synchronized (getDriver().getSYNCCommander())
    {
      response = getATHandler().getManufacturer();
      if (response.indexOf("ERROR") >= 0) return "N/A";
      response = response.replaceAll("\\s+OK\\s+", "");
      return response;
    }
  }

  /**
   * Returns the Model string.
   *
   * @return The Model string.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public String getModel() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    String response;
    synchronized (getDriver().getSYNCCommander())
    {
      response = getATHandler().getModel();
      if (response.indexOf("ERROR") >= 0) return "N/A";
      response = response.replaceAll("\\s+OK\\s+", "");
      return response;
    }
  }

  /**
   * Returns the Serial Number of the modem.
   *
   * @return The Serial Number.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public String getSerialNo() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    String response;
    synchronized (getDriver().getSYNCCommander())
    {
      response = getATHandler().getSerialNo();
      if (response.indexOf("ERROR") >= 0) return "N/A";
      response = response.replaceAll("\\s+OK\\s+", "");
      return response;
    }
  }

  /**
   * Returns the IMSI (International Mobile Subscriber Identity) number.
   * <p>
   * This number is stored in the SIM. Since this number may be used for
   * several illegal activities, the method is remarked. If you wish to see
   * your IMSI, just uncomment the method.
   *
   * @return The IMSI.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public String getImsi() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (Service.getInstance().getSettings().MASK_IMSI) return "** MASKED **";
    synchronized (getDriver().getSYNCCommander())
    {
      String response;
      response = getATHandler().getImsi();
      if (response.indexOf("ERROR") >= 0) return "N/A";
      response = response.replaceAll("\\s+OK\\s+", "");
      return response;
    }
  }

  /**
   * Returns the modem's firmware version.
   *
   * @return The modem's firmware version.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public String getSwVersion() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    String response;
    synchronized (getDriver().getSYNCCommander())
    {
      response = getATHandler().getSwVersion();
      if (response.indexOf("ERROR") >= 0) return "N/A";
      response = response.replaceAll("\\s+OK\\s+", "");
      return response;
    }
  }

  boolean getGprsStatus() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    synchronized (getDriver().getSYNCCommander())
    {
      return (getATHandler().getGprsStatus().matches("\\+CGATT[\\p{ASCII}]*1\\sOK\\s"));
    }
  }

  /**
   * Returns the battery level (0-100).
   *
   * @return The battery level.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public int getBatteryLevel() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    String response;
    synchronized (getDriver().getSYNCCommander())
    {
      response = getATHandler().getBatteryLevel();
      if (response.indexOf("ERROR") >= 0) return 0;
      Matcher m = Pattern.compile("\\+CBC: (\\d+),\\s*(\\d+)").matcher(response);
      if (m.find()) return Integer.parseInt(m.group(2));
      return 0;
    }
  }

  /**
   * Returns the signal level (RSSI).
   *
   * @return The signal level.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public int getSignalLevel() throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    String response;
    StringTokenizer tokens;
    Integer rssi_code;
    synchronized (getDriver().getSYNCCommander())
    {
      response = getATHandler().getSignalLevel();
      if (response.indexOf("ERROR") >= 0) return 0;
      response = response.replaceAll("\\s+OK\\s+", "");
      tokens = new StringTokenizer(response, ":,");
      tokens.nextToken();
      rssi_code = Integer.parseInt(tokens.nextToken().trim());
      if (rssi_code == 99) return 99;
      else return (-113 + 2 * rssi_code);
    }
  }

  /**
   * Returns the SMSC number used by SMSLib. If no SMSC number has been set
   * with setSmscNumber() call, this method returns nothing.
   *
   * @return The SMSC number.
   * @see #setSmscNumber(String)
   */
  public String getSmscNumber()
  {
    return this.smscNumber;
  }

  /**
   * Sets the SMSC number used by SMSLib.
   * <p>
   * Note that in most cases, you will <b>not</b> need to call this method, as
   * the modem knows the SMSC it should use by reading the SIM card. In rare
   * cases when the modem/phone cannot read the SMSC from the SIM card or you
   * would like to set a different SMSC than the default, you can use this
   * method.
   *
   * @param mySmscNumber
   *            The SMSC number used from now on.
   */
  public void setSmscNumber(String mySmscNumber)
  {
    this.smscNumber = mySmscNumber;
  }

  /**
   * Returns the custom modem init string (if any).
   *
   * @return the custom init string.
   * @see #setCustomInitString(String)
   */
  public String getCustomInitString()
  {
    return this.customInitString;
  }

  /**
   * Sets the custom modem init string. The init string (if defined) is sent
   * to the modem right before the SIM PIN check.
   *
   * @param myCustomInitString
   *            The custom init string.
   * @see #getCustomInitString()
   */
  public void setCustomInitString(String myCustomInitString)
  {
    this.customInitString = myCustomInitString;
  }

  /**
   * Send a custom AT command to the modem and returns the response received.
   *
   * @param atCommand
   *            The AT Command.
   * @return The modem's response.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public String sendCustomATCommand(String atCommand) throws GatewayException, TimeoutException, IOException, InterruptedException
  {
    synchronized (getDriver().getSYNCCommander())
    {
      return getATHandler().sendCustomATCommand(atCommand);
    }
  }

  /**
   * Send an Unstructured Supplementary Service Data command. Default is
   * assumed for whether the command is interactive.
   *
   * @param ussdCommand
   *            the USSD command to send
   * @return the network's response to the command
   * @throws GatewayException
   * @throws TimeoutException
   * @throws IOException
   * @throws InterruptedException
   */
  @Override
  public String sendUSSDCommand(String ussdCommand) throws GatewayException, TimeoutException, IOException, InterruptedException
  {
    synchronized (getDriver().getSYNCCommander())
    {
      return getATHandler().sendUSSDCommand(ussdCommand);
    }
  }

  /**
   * Send an Unstructured Supplementary Service Data command.
   *
   * @param ussdCommand
   *            the USSD command to send
   * @param interactive
   *            whether the USSD session should be left open
   * @return the network's response to the command
   * @throws GatewayException
   * @throws TimeoutException
   * @throws IOException
   * @throws InterruptedException
   */
  @Override
  public String sendUSSDCommand(String ussdCommand, boolean interactive) throws GatewayException, TimeoutException, IOException, InterruptedException
  {
    synchronized (getDriver().getSYNCCommander())
    {
      return getATHandler().sendUSSDCommand(ussdCommand, interactive);
    }
  }

  public boolean sendUSSDRequest(USSDRequest request) throws GatewayException, TimeoutException, IOException, InterruptedException
  {
    synchronized (getDriver().getSYNCCommander())
    {
      return getATHandler().sendUSSDRequest(Integer.toString(request.getResultPresentation().getNumeric()), request.getContent(), Integer.toString(request.getDcs().getNumeric()));
    }
  }

  protected AModemDriver getDriver()
  {
    return this.driver;
  }

  protected void setDriver(AModemDriver myDriver)
  {
    this.driver = myDriver;
  }

  protected AATHandler getAtHandler()
  {
    return this.atHandler;
  }

  protected void setAtHandler(AATHandler myAtHandler)
  {
    this.atHandler = myAtHandler;
  }

  protected String getModemDevice()
  {
    return this.modemDevice;
  }

  protected void setModemDevice(String myModemDevice)
  {
    this.modemDevice = myModemDevice;
  }

  protected int getModemParms()
  {
    return this.modemParms;
  }

  protected void setModemParms(int myModemParms)
  {
    this.modemParms = myModemParms;
  }

  protected int getAgeInHours(Date messageDate)
  {
    Calendar cal = Calendar.getInstance();
    cal.setTime(new java.util.Date());
    long now = cal.getTimeInMillis();
    cal.setTime(messageDate);
    long past = cal.getTimeInMillis();
    return (int) ((now - past) / (60 * 60 * 1000));
  }

  @Override
  public int getQueueSchedulingInterval()
  {
    return Service.getInstance().getSettings().QUEUE_SCHEDULING_INTERNAL;
  }
}
TOP

Related Classes of org.smslib.modem.ModemGateway

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.