blob: 66d0d4996dae7e9a27340bcf8c667d2b878efac2 [file] [log] [blame]
/****************************************************************
* 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.james.mime4j.codec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Random;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.james.mime4j.io.InputStreams;
import org.apache.james.mime4j.util.ContentUtil;
import org.junit.Assert;
import org.junit.Test;
public class Base64InputStreamTest {
private static Base64InputStream create(final String s) {
return new Base64InputStream(InputStreams.createAscii(s));
}
private static Base64InputStream createStrict(final String s) {
return new Base64InputStream(InputStreams.createAscii(s), true);
}
private static Base64InputStream create(final byte[] b) {
return new Base64InputStream(InputStreams.create(b));
}
private static byte[] readBin(final InputStream is) throws IOException {
return IOUtils.toByteArray(is);
}
private static String readText(final InputStream is) throws IOException {
return ContentUtil.toAsciiString(IOUtils.toByteArray(is));
}
@Test
public void testDecode() throws IOException {
Base64InputStream decoder;
/*
* Simple initial test.
*/
decoder = create("VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlIQ==");
Assert.assertEquals("This is the plain text message!", readText(decoder));
/*
* Test encoded text padded once, twice and not at all.
*/
decoder = create(("VGhpcyBpcyBhIHRleHQgd2hpY2ggaGFzIHRvIGJl"
+ "IHBhZGRlZCBvbmNlLi4="));
Assert.assertEquals("This is a text which has to be padded once..", readText(decoder));
decoder = create(("VGhpcyBpcyBhIHRleHQgd2hpY2ggaGFzIHRvIGJl"
+ "IHBhZGRlZCB0d2ljZQ=="));
Assert.assertEquals("This is a text which has to be padded twice", readText(decoder));
decoder = create(("VGhpcyBpcyBhIHRleHQgd2hpY2ggd2lsbCBub3Qg"
+ "YmUgcGFkZGVk"));
Assert.assertEquals("This is a text which will not be padded", readText(decoder));
/*
* Test that non base64 characters are ignored.
*/
decoder = create((" &% VGhp\r\ncyBp\r\ncyB0aGUgcGxhaW4g "
+ " \tdGV4dCBtZ?!XNzY*WdlIQ=="));
Assert.assertEquals("This is the plain text message!", readText(decoder));
/*
* Test that the bytes 0-255 shifted 0, 1 and 2 positions are
* decoded properly.
*/
String s1 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCU"
+ "mJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0"
+ "xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3Bxc"
+ "nN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeY"
+ "mZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6"
+ "/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5O"
+ "Xm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==";
String s2 = "AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSY"
+ "nKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE"
+ "1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc"
+ "3R1dnd4eXp7fH1+f4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZ"
+ "mpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/"
+ "AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3+Dh4uPk5e"
+ "bn6Onq6+zt7u/w8fLz9PX29/j5+vv8/f7/AA==";
String s3 = "AgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJic"
+ "oKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU"
+ "5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzd"
+ "HV2d3h5ent8fX5/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJma"
+ "m5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8D"
+ "BwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7f4OHi4+Tl5u"
+ "fo6err7O3u7/Dx8vP09fb3+Pn6+/z9/v8AAQ==";
byte[] bytes;
decoder = create(s1);
bytes = readBin(decoder);
for (int i = 0; i < bytes.length; i++) {
Assert.assertEquals("Position " + i, bytes[i], (byte) i);
}
decoder = create(s2);
bytes = readBin(decoder);
for (int i = 0; i < bytes.length; i++) {
Assert.assertEquals("Position " + i, bytes[i], (byte) (i + 1));
}
decoder = create(s3);
bytes = readBin(decoder);
for (int i = 0; i < bytes.length; i++) {
Assert.assertEquals("Position " + i, bytes[i], (byte) (i + 2));
}
}
@Test
public void testDecodePrematureClose() throws IOException {
Base64InputStream decoder = create("VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlIQ==");
Assert.assertEquals('T', decoder.read());
Assert.assertEquals('h', decoder.read());
decoder.close();
try {
decoder.read();
Assert.fail();
} catch (IOException expected) {
}
}
@Test
public void testRoundtripWithVariousBufferSizes() throws Exception {
byte[] data = new byte[3719];
new Random(0).nextBytes(data);
ByteArrayOutputStream eOut = new ByteArrayOutputStream();
EncoderUtil.encodeB(InputStreams.create(data), eOut);
byte[] encoded = eOut.toByteArray();
for (int bufferSize = 1; bufferSize <= 1009; bufferSize++) {
Base64InputStream decoder = create(encoded);
ByteArrayOutputStream dOut = new ByteArrayOutputStream();
final byte[] buffer = new byte[bufferSize];
int inputLength;
while (-1 != (inputLength = decoder.read(buffer))) {
dOut.write(buffer, 0, inputLength);
}
byte[] decoded = dOut.toByteArray();
Assert.assertEquals(data.length, decoded.length);
for (int i = 0; i < data.length; i++) {
Assert.assertEquals(data[i], decoded[i]);
}
decoder.close();
}
}
/**
* Tests {@link InputStream#read()}
*/
@Test
public void testReadInt() throws Exception {
Base64InputStream decoder = create("VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlIQ==");
ByteArrayOutputStream out = new ByteArrayOutputStream();
while (true) {
int x = decoder.read();
if (x == -1)
break;
out.write(x);
}
decoder.close();
Assert.assertEquals("This is the plain text message!",
ContentUtil.toAsciiString(out.toByteArray()));
}
/**
* Tests {@link InputStream#read(byte[], int, int)} with various offsets
*/
@Test
public void testReadOffset() throws Exception {
Base64InputStream decoder = create("VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlIQ==");
byte[] data = new byte[36];
for (int i = 0; ; ) {
int bytes = decoder.read(data, i, 5);
if (bytes == -1)
break;
i += bytes;
}
decoder.close();
Assert.assertEquals("This is the plain text message!\0\0\0\0\0",
ContentUtil.toAsciiString(data));
}
@Test
public void testStrictUnexpectedEof() throws Exception {
Base64InputStream decoder = createStrict("VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlI");
try {
ContentUtil.copy(decoder, new NullOutputStream());
Assert.fail();
} catch (IOException expected) {
Assert.assertTrue(expected.getMessage().toLowerCase().contains(
"end of base64 stream"));
}
}
@Test
public void testLenientUnexpectedEof() throws Exception {
Base64InputStream decoder = create("VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlI");
byte[] buf = ContentUtil.buffer(decoder);
Assert.assertEquals("This is the plain text message", ContentUtil.toAsciiString(buf));
}
@Test
public void testStrictUnexpectedPad() throws Exception {
Base64InputStream decoder = createStrict("VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlI=");
try {
ContentUtil.copy(decoder, new NullOutputStream());
Assert.fail();
} catch (IOException expected) {
Assert.assertTrue(expected.getMessage().toLowerCase().contains("pad"));
}
}
@Test
public void testLenientUnexpectedPad() throws Exception {
Base64InputStream decoder = create("VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlI=");
byte[] buf = ContentUtil.buffer(decoder);
Assert.assertEquals("This is the plain text message", ContentUtil.toAsciiString(buf));
}
}