/*
  $Id: LdapUtil.java 2217 2012-01-23 19:56:35Z dfisher $

  Copyright (C) 2003-2010 Virginia Tech.
  All rights reserved.

  SEE LICENSE FOR MORE INFORMATION

  Author:  Middleware Services
  Email:   middleware@vt.edu
  Version: $Revision: 2217 $
  Updated: $Date: 2012-01-23 20:56:35 +0100 (Mon, 23 Jan 2012) $
*/
package edu.vt.middleware.ldap;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.regex.Pattern;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <code>LdapUtil</code> provides helper methods for <code>Ldap</code>.
 *
 * @author  Middleware Services
 * @version  $Revision: 2217 $ $Date: 2012-01-23 20:56:35 +0100 (Mon, 23 Jan 2012) $
 */
public final class LdapUtil
{

  /** Size of buffer in bytes to use when reading files. */
  private static final int READ_BUFFER_SIZE = 128;

  /** Pattern to match ipv4 addresses. */
  private static final Pattern IPV4_PATTERN =
    Pattern.compile(
      "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)" +
      "(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");

  /** Pattern to match ipv6 addresses. */
  private static final Pattern IPV6_STD_PATTERN =
    Pattern.compile("^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");

  /** Pattern to match ipv6 hex compressed addresses. */
  private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
    Pattern.compile(
      "^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::" +
      "((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$");


  /** Default constructor. */
  private LdapUtil() {}


  /**
   * This checks a credential to ensure it is the right type and it is not
   * empty. A credential can be of type String, char[], or byte[].
   *
   * @param  credential  <code>Object</code> to check
   *
   * @return  <code>boolean</code> - whether the credential is valid
   */
  public static boolean checkCredential(final Object credential)
  {
    boolean answer = false;
    if (credential != null) {
      if (credential instanceof String) {
        final String string = (String) credential;
        if (!"".equals(string)) {
          answer = true;
        }
      } else if (credential instanceof char[]) {
        final char[] array = (char[]) credential;
        if (array.length != 0) {
          answer = true;
        }
      } else if (credential instanceof byte[]) {
        final byte[] array = (byte[]) credential;
        if (array.length != 0) {
          answer = true;
        }
      }
    }
    return answer;
  }


  /**
   * This will convert the supplied value to a base64 encoded string. Returns
   * null if the bytes cannot be encoded.
   *
   * @param  value  <code>byte[]</code> to base64 encode
   *
   * @return  <code>String</code>
   */
  public static String base64Encode(final byte[] value)
  {
    String encodedValue = null;
    if (value != null) {
      try {
        encodedValue = new String(
          Base64.encodeBase64(value),
          LdapConstants.DEFAULT_CHARSET);
      } catch (UnsupportedEncodingException e) {
        final Log logger = LogFactory.getLog(LdapUtil.class);
        if (logger.isErrorEnabled()) {
          logger.error(
            "Could not encode value using " + LdapConstants.DEFAULT_CHARSET);
        }
      }
    }
    return encodedValue;
  }


  /**
   * This will convert the supplied value to a base64 encoded string. Returns
   * null if the string cannot be encoded.
   *
   * @param  value  <code>String</code> to base64 encode
   *
   * @return  <code>String</code>
   */
  public static String base64Encode(final String value)
  {
    String encodedValue = null;
    if (value != null) {
      try {
        encodedValue = base64Encode(
          value.getBytes(LdapConstants.DEFAULT_CHARSET));
      } catch (UnsupportedEncodingException e) {
        final Log logger = LogFactory.getLog(LdapUtil.class);
        if (logger.isErrorEnabled()) {
          logger.error(
            "Could not encode value using " + LdapConstants.DEFAULT_CHARSET);
        }
      }
    }
    return encodedValue;
  }


  /**
   * This will decode the supplied value as a base64 encoded string to a byte[].
   *
   * @param  value  <code>Object</code> to base64 encode
   *
   * @return  <code>String</code>
   */
  public static byte[] base64Decode(final String value)
  {
    byte[] decodedValue = null;
    if (value != null) {
      decodedValue = Base64.decodeBase64(value.getBytes());
    }
    return decodedValue;
  }


  /**
   * Reads the data at the supplied URL and returns it as a byte array.
   *
   * @param  url  <code>URL</code> to read
   *
   * @return  <code>byte[]</code> read from URL
   *
   * @throws  IOException  if an error occurs reading data
   */
  public static byte[] readURL(final URL url)
    throws IOException
  {
    return readInputStream(url.openStream());
  }


  /**
   * Reads the data in the supplied stream and returns it as a byte array.
   *
   * @param  is  <code>InputStream</code> to read
   *
   * @return  <code>byte[]</code> read from the stream
   *
   * @throws  IOException  if an error occurs reading data
   */
  public static byte[] readInputStream(final InputStream is)
    throws IOException
  {
    final ByteArrayOutputStream data = new ByteArrayOutputStream();
    try {
      final byte[] buffer = new byte[READ_BUFFER_SIZE];
      int length;
      while ((length = is.read(buffer)) != -1) {
        data.write(buffer, 0, length);
      }
    } finally {
      is.close();
      data.close();
    }
    return data.toByteArray();
  }


  /**
   * Returns whether the supplied string represents an IP address. Matches both
   * IPv4 and IPv6 addresses.
   *
   * @param  s  to match
   *
   * @return  whether the supplied string represents an IP address
   */
  public static boolean isIPAddress(final String s)
  {
    return s != null &&
      (IPV4_PATTERN.matcher(s).matches() ||
       IPV6_STD_PATTERN.matcher(s).matches() ||
       IPV6_HEX_COMPRESSED_PATTERN.matcher(s).matches());
  }
}
