All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.plivo.api.util.Utils Maven / Gradle / Ivy

Go to download

A Java SDK to make voice calls & send SMS using Plivo and to generate Plivo XML

There is a newer version: 5.46.0
Show newest version
package com.plivo.api.util;

import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.plivo.api.exceptions.PlivoXmlException;

public class Utils {

  private static final Pattern timePattern = Pattern.compile("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,6})?)?$");

  public static boolean allNotNull(Object... objects) {
    return Stream.of(objects)
      .noneMatch(Objects::isNull);
  }

  public static boolean isSubaccountIdValid(String id) {
    return id != null && id.startsWith("SA") && id.length() == 20;
  }

  public static boolean isAccountIdValid(String id) {
    return id != null && id.startsWith("MA") && id.length() == 20;
  }

  public static boolean anyNotNull(Object... objects) {
    return Stream.of(objects)
      .anyMatch(Objects::nonNull);
  }

  public static boolean isValidTimeString(String time) {
    return timePattern.matcher(time).matches();
  }

  public static boolean isNonEmptyString(String str) {
    return !(str == null || str.trim().isEmpty());
  }


  public static Map objectToMap(ObjectMapper objectMapper, Object object) {
    Map origMap = objectMapper.convertValue(object, Map.class);
    Map map = new LinkedHashMap<>();
    for (Entry entry : origMap.entrySet()) {
      if (entry.getValue() != null) {
        if (entry.getValue() instanceof Map) {
          Map innerEntries = objectMapper.convertValue(entry.getValue(), Map.class);
          for (Entry innerEntry : innerEntries.entrySet()) {
            map.put(entry.getKey() + innerEntry.getKey(), innerEntry.getValue());
          }
        } else {
          map.put(entry.getKey(), entry.getValue());
        }
      }
    }
    return map;
  }

  private final static String SIGNATURE_ALGORITHM = "HmacSHA256";

  public static String computeSignature(String url, String nonce, String authToken)
    throws NoSuchAlgorithmException, InvalidKeyException, MalformedURLException, UnsupportedEncodingException {
    if (!allNotNull(url, nonce, authToken)) {
      throw new IllegalArgumentException("url, nonce and authToken must be non-null");
    }

    URL parsedURL = new URL(url);
    String baseUrl = parsedURL.getProtocol() + "://" + parsedURL.getHost();
    if (parsedURL.getPort() != -1) {
      baseUrl += ":" + Integer.toString(parsedURL.getPort());
    }
    baseUrl += parsedURL.getPath();
    String payload = baseUrl + nonce;
    SecretKeySpec signingKey = new SecretKeySpec(authToken.getBytes("UTF-8"), SIGNATURE_ALGORITHM);
    Mac mac = Mac.getInstance(SIGNATURE_ALGORITHM);
    mac.init(signingKey);
    return new String(Base64.getEncoder().encode(mac.doFinal(payload.getBytes("UTF-8"))));
  }

  public static boolean validateSignature(String url, String nonce, String signature, String authToken)
    throws NoSuchAlgorithmException, InvalidKeyException, MalformedURLException, UnsupportedEncodingException {
    return computeSignature(url, nonce, authToken).equals(signature);
  }

  public static String generateUrl(String url, String method, Map params) throws MalformedURLException, UnsupportedEncodingException {
    String decodedUrl = java.net.URLDecoder.decode(url, StandardCharsets.UTF_8.name());
    URL parsedURL = new URL(decodedUrl);
    String paramString = "";
    List keys = new ArrayList(params.keySet());
    String uri = parsedURL.getProtocol() + "://" + parsedURL.getHost() + parsedURL.getPath();
    int queryParamLength = 0;
    try {
      queryParamLength = parsedURL.getQuery().length();
      if (params.size() > 0 || queryParamLength > 0) {
        uri += "?";
      }
    } catch (Exception e) {
      queryParamLength = 0;
      uri += "?";
    }
    if (queryParamLength > 0) {
      if (method == "GET") {
        Map queryParamMap = getMapFromQueryString(parsedURL.getQuery());
        for (Entry entry : params.entrySet()) {
          queryParamMap.put(entry.getKey(), entry.getValue());
        }
        uri += GetSortedQueryParamString(queryParamMap, true);
      } else {
        uri += GetSortedQueryParamString(getMapFromQueryString(parsedURL.getQuery()), true) + "." + GetSortedQueryParamString(params, false);
        uri = uri.replace(".$", "");
      }
    } else {
      if (method == "GET") {
        uri += GetSortedQueryParamString(params, true);
      } else {
        uri += GetSortedQueryParamString(params, false);
      }
    }
    return uri;
  }

  public static Map getMapFromQueryString(String query) {
    Map params = null;
    if (query.length() == 0) {
      return params;
    }
    params = Arrays.stream(query.split("&")).map(s -> s.split("=", 2)).collect(Collectors.toMap(a -> a[0], a -> a.length > 1 ? a[1] : ""));
    return params;
  }

  public static String GetSortedQueryParamString(Map params, boolean queryParams) {
    String url = "";
    List keys = new ArrayList(params.keySet());
    Collections.sort(keys);
    if (queryParams) {
      for (String key : keys) {
        url += key + "=" + params.get(key) + "&";
      }
      url = url.substring(0, url.length() - 1);
    } else {
      for (String key : keys) {
        url += key + params.get(key);
      }
    }
    return url;
  }

  public static String computeSignatureV3(String url, String nonce, String authToken, String method, Map params)
    throws NoSuchAlgorithmException, InvalidKeyException, MalformedURLException, UnsupportedEncodingException {
    if (!allNotNull(url, nonce, authToken)) {
      throw new IllegalArgumentException("url, nonce and authToken must be non-null");
    }

    URL parsedURL = new URL(url);
    String payload = generateUrl(url, method, params) + "." + nonce;
    SecretKeySpec signingKey = new SecretKeySpec(authToken.getBytes("UTF-8"), SIGNATURE_ALGORITHM);
    Mac mac = Mac.getInstance(SIGNATURE_ALGORITHM);
    mac.init(signingKey);
    return new String(Base64.getEncoder().encode(mac.doFinal(payload.getBytes("UTF-8"))));
  }

  public static boolean validateSignatureV3(String url, String nonce, String signature, String authToken, String method, Map... params)
    throws NoSuchAlgorithmException, InvalidKeyException, MalformedURLException, UnsupportedEncodingException {
    Map parameters = new HashMap();
    if(params.length > 0){
      parameters = params[0];
    }
    List splitSignature = Arrays.asList(signature.split(","));
    return splitSignature.contains(computeSignatureV3(url, nonce, authToken, method, parameters));
  }

  private static Map> getLanguageVoices() {
    Map> languageVoices = new HashMap<>();
    languageVoices.put("arb", new ArrayList(Arrays.asList("Zeina")));
    languageVoices.put("cmn-CN", new ArrayList(Arrays.asList("Zhiyu")));
    languageVoices.put("da-DK", new ArrayList(Arrays.asList("Naja", "Mads")));
    languageVoices.put("nl-NL", new ArrayList(Arrays.asList("Lotte", "Ruben")));
    languageVoices.put("en-AU", new ArrayList(Arrays.asList("Nicole", "Russell")));
    languageVoices.put("en-GB", new ArrayList(Arrays.asList("Amy", "Emma", "Brian")));
    languageVoices.put("en-IN", new ArrayList(Arrays.asList("Raveena", "Aditi")));
    languageVoices.put("en-US", new ArrayList(Arrays.asList("Joanna", "Salli", "Kendra", "Kimberly", "Ivy", "Matthew", "Justin", "Joey")));
    languageVoices.put("en-GB-WLS", new ArrayList(Arrays.asList("Geraint")));
    languageVoices.put("fr-CA", new ArrayList(Arrays.asList("Chantal", "Chantal")));
    languageVoices.put("fr-FR", new ArrayList(Arrays.asList("Léa", "Céline", "Mathieu")));
    languageVoices.put("de-DE", new ArrayList(Arrays.asList("Vicki", "Hans")));
    languageVoices.put("hi-IN", new ArrayList(Arrays.asList("Aditi")));
    languageVoices.put("is-IS", new ArrayList(Arrays.asList("Dóra", "Karl")));
    languageVoices.put("it-IT", new ArrayList(Arrays.asList("Carla", "Giorgio")));
    languageVoices.put("ja-JP", new ArrayList(Arrays.asList("Mizuki", "Takumi")));
    languageVoices.put("ko-KR", new ArrayList(Arrays.asList("Seoyeon")));
    languageVoices.put("nb-NO", new ArrayList(Arrays.asList("Liv")));
    languageVoices.put("pl-PL", new ArrayList(Arrays.asList("Ewa", "Maja", "Jacek", "Jan")));
    languageVoices.put("pt-BR", new ArrayList(Arrays.asList("Vitória", "Ricardo")));
    languageVoices.put("pt-PT", new ArrayList(Arrays.asList("Inês", "Cristiano")));
    languageVoices.put("ro-RO", new ArrayList(Arrays.asList("Carmen")));
    languageVoices.put("ru-RU", new ArrayList(Arrays.asList("Tatyana", "Maxim")));
    languageVoices.put("es-ES", new ArrayList(Arrays.asList("Conchita", "Lucia", "Enrique")));
    languageVoices.put("es-MX", new ArrayList(Arrays.asList("Mia")));
    languageVoices.put("es-US", new ArrayList(Arrays.asList("Penélope", "Miguel")));
    languageVoices.put("sv-SE", new ArrayList(Arrays.asList("Astrid")));
    languageVoices.put("tr-TR", new ArrayList(Arrays.asList("Filiz")));
    languageVoices.put("cy-GB", new ArrayList(Arrays.asList("Gwyneth")));

    return languageVoices;
  }

  public static void validateLanguageVoice(String language, String voice) throws PlivoXmlException {
    String[] voiceParts = voice.split("\\.");
    System.out.println(language);
    if (voiceParts.length != 2 || !voiceParts[0].equals("Polly")) {
      throw new PlivoXmlException("XML Validation Error: Invalid language. Voice " + voice + " is not valid. " +
        "Refer  for the list of supported voices.");
    }

    Map> languageVoices = getLanguageVoices();
    // Validate supported languages.
    if (languageVoices.get(language) == null || languageVoices.get(language).isEmpty()) {
      throw new PlivoXmlException("XML Validation Error: Invalid language. Language " + language + " is not supported.");
    }

    // Transform the available language voices and the voice name into a common format.
    List availableLanguageVoices = languageVoices.get(language);
    for (int i = 0; i < availableLanguageVoices.size(); i++) {
      availableLanguageVoices.set(i, transformString(availableLanguageVoices.get(i)));
    }
    String transformedVoiceName = transformString(voiceParts[1]);

    if (!voiceParts[1].equals("*") && !availableLanguageVoices.contains(transformedVoiceName)) {
      throw new PlivoXmlException("XML Validation Error:  voice '" + voice +
        "' is not valid. Refer  for list of supported voices.");
    }
  }

  public static String transformString(String s) {
    String transformedString;
    // Replace all accented characters with comparable english alphabets
    transformedString = Normalizer.normalize(s.trim(), Normalizer.Form.NFD);
    Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
    transformedString = pattern.matcher(transformedString).replaceAll("");

    // To title case and replace spaces with '_'
    transformedString = (new ArrayList<>(Arrays.asList(transformedString.toLowerCase().split(" "))))
      .stream()
      .map(word -> Character.toTitleCase(word.charAt(0)) + word.substring(1))
      .collect(Collectors.joining("_"));
    return transformedString;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy