content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You 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.apache.camel.component.smpp;
  
  import java.nio.charset.Charset;
  import java.nio.charset.StandardCharsets;
  
  import org.apache.camel.Message;
  import org.jsmpp.bean.Alphabet;
  import org.jsmpp.extra.NegativeResponseException;
  import org.jsmpp.session.SMPPSession;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public abstract class SmppSmCommand extends AbstractSmppCommand {
  
      // FIXME: these constants should be defined somewhere in jSMPP:
      public static final int SMPP_NEG_RESPONSE_MSG_TOO_LONG = 1;
  
      protected Charset ascii = StandardCharsets.US_ASCII;
      protected Charset latin1 = StandardCharsets.ISO_8859_1;
      protected Charset defaultCharset;
  
      private final Logger logger = LoggerFactory.getLogger(SmppSmCommand.class);
  
      public SmppSmCommand(SMPPSession session, SmppConfiguration config) {
          super(session, config);
          defaultCharset = Charset.forName(config.getEncoding());
      }
  
      protected byte[][] splitBody(Message message) throws SmppException {
          byte[] shortMessage = getShortMessage(message);
          SmppSplitter splitter = createSplitter(message);
          byte[][] segments = splitter.split(shortMessage);
          if (segments.length > 1) {
              // Message body is split into multiple parts,
              // check if this is permitted
              SmppSplittingPolicy policy = getSplittingPolicy(message);
              switch (policy) {
                  case ALLOW:
                      return segments;
                  case TRUNCATE:
                      return new byte[][] { java.util.Arrays.copyOfRange(shortMessage, 0, segments[0].length) };
                  case REJECT:
                      // FIXME - JSMPP needs to have an enum of the negative response
                      // codes instead of just using them like this
                      NegativeResponseException nre = new NegativeResponseException(SMPP_NEG_RESPONSE_MSG_TOO_LONG);
                      throw new SmppException(nre);
                  default:
                      throw new SmppException("Unknown splitting policy: " + policy);
              }
          } else {
              return segments;
          }
      }
  
      private SmppSplittingPolicy getSplittingPolicy(Message message) throws SmppException {
          if (message.getHeaders().containsKey(SmppConstants.SPLITTING_POLICY)) {
              String policyName = message.getHeader(SmppConstants.SPLITTING_POLICY, String.class);
              return SmppSplittingPolicy.fromString(policyName);
          }
          return config.getSplittingPolicy();
      }
  
      protected SmppSplitter createSplitter(Message message) throws SmppException {
  
          SmppSplitter splitter;
          // use the splitter if provided via header
          if (message.getHeaders().containsKey(SmppConstants.DATA_SPLITTER)) {
              splitter = message.getHeader(SmppConstants.DATA_SPLITTER, SmppSplitter.class);
              if (null != splitter) {
                  return splitter;
              }
              throw new SmppException("Invalid splitter given. Must be instance of SmppSplitter");
          }
          Alphabet alphabet = determineAlphabet(message);
          String body = message.getBody(String.class);
  
          if (SmppUtils.is8Bit(alphabet)) {
              splitter = new Smpp8BitSplitter(body.length());
          } else if (alphabet == Alphabet.ALPHA_UCS2) {
              splitter = new SmppUcs2Splitter(body.length());
          } else {
              splitter = new SmppDefaultSplitter(body.length());
          }
          return splitter;
      }
  
      protected final byte[] getShortMessage(Message message) {
          if (has8bitDataCoding(message)) {
              return message.getBody(byte[].class);
          } else {
              byte providedAlphabet = getProvidedAlphabet(message);
              Alphabet determinedAlphabet = determineAlphabet(message);
              Charset charset = determineCharset(message, providedAlphabet, determinedAlphabet.value());
              String body = message.getBody(String.class);
              return body.getBytes(charset);
          }
      }
  
      private static boolean has8bitDataCoding(Message message) {
          Byte dcs = message.getHeader(SmppConstants.DATA_CODING, Byte.class);
          if (dcs != null) {
              return SmppUtils.is8Bit(Alphabet.parseDataCoding(dcs.byteValue()));
          } else {
              Byte alphabet = message.getHeader(SmppConstants.ALPHABET, Byte.class);
              return alphabet != null && SmppUtils.is8Bit(Alphabet.valueOf(alphabet));
          }
      }
  
      private byte getProvidedAlphabet(Message message) {
          byte alphabet = config.getAlphabet();
          if (message.getHeaders().containsKey(SmppConstants.ALPHABET)) {
              alphabet = message.getHeader(SmppConstants.ALPHABET, Byte.class);
          }
  
          return alphabet;
      }
  
      private Charset getCharsetForMessage(Message message) {
          if (message.getHeaders().containsKey(SmppConstants.ENCODING)) {
              String encoding = message.getHeader(SmppConstants.ENCODING, String.class);
              if (Charset.isSupported(encoding)) {
                  return Charset.forName(encoding);
              } else {
                  logger.warn("Unsupported encoding \"{}\" requested in header.", encoding);
              }
          }
          return null;
      }
  
      private Charset determineCharset(Message message, byte providedAlphabet, byte determinedAlphabet) {
          Charset result = getCharsetForMessage(message);
          if (result != null) {
              return result;
          }
  
          if (providedAlphabet == Alphabet.ALPHA_UCS2.value()
                  || providedAlphabet == SmppConstants.UNKNOWN_ALPHABET && determinedAlphabet == Alphabet.ALPHA_UCS2.value()) {
              // change charset to use multilang messages
              return StandardCharsets.UTF_16BE;
          }
  
          return defaultCharset;
      }
  
      private Alphabet determineAlphabet(Message message) {
          String body = message.getBody(String.class);
          byte alphabet = getProvidedAlphabet(message);
          Charset charset = getCharsetForMessage(message);
          if (charset == null) {
              charset = defaultCharset;
          }
  
          Alphabet alphabetObj;
          if (alphabet == SmppConstants.UNKNOWN_ALPHABET) {
              alphabetObj = Alphabet.ALPHA_UCS2;
              if (isLatin1Compatible(charset)) {
                  byte[] messageBytes = body.getBytes(charset);
                  if (SmppUtils.isGsm0338Encodeable(messageBytes)) {
                      alphabetObj = Alphabet.ALPHA_DEFAULT;
                  }
              }
          } else {
              alphabetObj = Alphabet.valueOf(alphabet);
          }
  
          return alphabetObj;
      }
  
      private boolean isLatin1Compatible(Charset c) {
          if (c.equals(ascii) || c.equals(latin1)) {
              return true;
          }
          return false;
      }
  
      protected byte getRegisterDeliveryFlag(Message message) {
          if (message.getHeaders().containsKey(SmppConstants.REGISTERED_DELIVERY)) {
              return message.getHeader(SmppConstants.REGISTERED_DELIVERY, Byte.class);
          }
          return config.getRegisteredDelivery();
      }
  
      protected boolean getRequestsSingleDLR(Message message) {
          if (message.getHeaders().containsKey(SmppConstants.SINGLE_DLR)) {
              return message.getHeader(SmppConstants.SINGLE_DLR, Boolean.class);
          }
          return config.isSingleDLR();
      }
  }
  