package org.apereo.cas.util; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; /** * This is {@link CompressionUtils} * that encapsulates common compression calls and operations * in one spot. * * @author Misagh Moayyed mmoayyed@unicon.net * @since 4.1 */ public final class CompressionUtils { private static final Logger LOGGER = LoggerFactory.getLogger(CompressionUtils.class); private static final int INFLATED_ARRAY_LENGTH = 10000; /** * Private ctor for a utility class. */ private CompressionUtils() { } /** * Inflate the given byte array by {@link #INFLATED_ARRAY_LENGTH}. * * @param bytes the bytes * @return the array as a string with {@code UTF-8} encoding */ public static String inflate(final byte[] bytes) { final Inflater inflater = new Inflater(true); final byte[] xmlMessageBytes = new byte[INFLATED_ARRAY_LENGTH]; final byte[] extendedBytes = new byte[bytes.length + 1]; System.arraycopy(bytes, 0, extendedBytes, 0, bytes.length); extendedBytes[bytes.length] = 0; inflater.setInput(extendedBytes); try { final int resultLength = inflater.inflate(xmlMessageBytes); inflater.end(); if (!inflater.finished()) { throw new RuntimeException("buffer not large enough."); } inflater.end(); return new String(xmlMessageBytes, 0, resultLength, StandardCharsets.UTF_8); } catch (final DataFormatException e) { return null; } } /** * Deflate the given bytes using zlib. * * @param bytes the bytes * @return the converted string */ public static String deflate(final byte[] bytes) { final String data = new String(bytes, StandardCharsets.UTF_8); return deflate(data); } /** * Deflate the given string via a {@link java.util.zip.Deflater}. * * @param data the data * @return base64 encoded string */ public static String deflate(final String data) { final Deflater deflater = new Deflater(); deflater.setInput(data.getBytes(StandardCharsets.UTF_8)); deflater.finish(); final byte[] buffer = new byte[data.length()]; final int resultSize = deflater.deflate(buffer); final byte[] output = new byte[resultSize]; System.arraycopy(buffer, 0, output, 0, resultSize); return EncodingUtils.encodeBase64(output); } /** * Decode the byte[] in base64 to a string. * * @param bytes the data to encode * @return the new string */ public static String decodeByteArrayToString(final byte[] bytes) { final ByteArrayInputStream bais = new ByteArrayInputStream(bytes); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final byte[] buf = new byte[bytes.length]; try (InflaterInputStream iis = new InflaterInputStream(bais)) { int count = iis.read(buf); while (count != -1) { baos.write(buf, 0, count); count = iis.read(buf); } return new String(baos.toByteArray(), StandardCharsets.UTF_8); } catch (final Exception e) { LOGGER.error("Base64 decoding failed", e); return null; } } /** * First decode base64 String to byte array, then use ZipInputStream to revert the byte array to a * string. * * @param zippedBase64Str the zipped base 64 str * @return the string, or null */ public static String decompress(final String zippedBase64Str) { GZIPInputStream zi = null; try { final byte[] bytes = Base64.decodeBase64(zippedBase64Str); zi = new GZIPInputStream(new ByteArrayInputStream(bytes)); return IOUtils.toString(zi); } catch (final Exception e) { LOGGER.error(e.getMessage(), e); } finally { IOUtils.closeQuietly(zi); } return null; } /** * Use ZipOutputStream to zip text to byte array, then convert * byte array to base64 string, so it can be transferred via http request. * * @param srcTxt the src txt * @return the string in UTF-8 format and base64'ed, or null. */ public static String compress(final String srcTxt) { try { final ByteArrayOutputStream rstBao = new ByteArrayOutputStream(); final GZIPOutputStream zos = new GZIPOutputStream(rstBao); zos.write(srcTxt.getBytes()); IOUtils.closeQuietly(zos); final byte[] bytes = rstBao.toByteArray(); final String base64 = StringUtils.remove(Base64.encodeBase64String(bytes), '\0'); return new String(StandardCharsets.UTF_8.encode(base64).array(), StandardCharsets.UTF_8); } catch (final IOException e) { LOGGER.error(e.getMessage(), e); } return null; } }