/* * JBoss, Home of Professional Open Source. * Copyright 2014 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * Licensed 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 io.undertow.util; import io.undertow.server.DefaultByteBufferPool; import io.undertow.testutils.DefaultServer; import io.undertow.testutils.category.UnitTest; import org.junit.Assert; import org.junit.Test; import org.junit.experimental.categories.Category; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; /** * @author Stuart Douglas */ @Category(UnitTest.class) public class MimeDecodingTestCase { @Test public void testSimpleMimeDecodingWithPreamble() throws IOException { final String data = fixLineEndings(FileUtils.readFile(MimeDecodingTestCase.class, "mime1.txt")); TestPartHandler handler = new TestPartHandler(); MultipartParser.ParseState parser = MultipartParser.beginParse(DefaultServer.getBufferPool(), handler, "unique-boundary-1".getBytes(), "ISO-8859-1"); ByteBuffer buf = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)); parser.parse(buf); Assert.assertTrue(parser.isComplete()); Assert.assertEquals(2, handler.parts.size()); Assert.assertEquals("Here is some text.", handler.parts.get(0).data.toString()); Assert.assertEquals("Here is some more text.", handler.parts.get(1).data.toString()); Assert.assertEquals("text/plain", handler.parts.get(0).map.getFirst(Headers.CONTENT_TYPE)); } @Test public void testMimeDecodingWithUTF8Headers() throws IOException { final String data = fixLineEndings(FileUtils.readFile(MimeDecodingTestCase.class, "mime-utf8.txt")); TestPartHandler handler = new TestPartHandler(); MultipartParser.ParseState parser = MultipartParser.beginParse(DefaultServer.getBufferPool(), handler, "unique-boundary-1".getBytes(), "UTF-8"); ByteBuffer buf = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)); parser.parse(buf); Assert.assertTrue(parser.isComplete()); Assert.assertEquals(1, handler.parts.size()); Assert.assertEquals("Just some chinese characters I copied from the internet, no idea what it says.", handler.parts.get(0).data.toString()); Assert.assertEquals("text/plain", handler.parts.get(0).map.getFirst(Headers.CONTENT_TYPE)); Assert.assertEquals("attachment; filename=个专为语文教学而设计的电脑软件.txt", handler.parts.get(0).map.getFirst(Headers.CONTENT_DISPOSITION)); } @Test public void testSimpleMimeDecodingWithoutPreamble() throws IOException { final String data = fixLineEndings(FileUtils.readFile(MimeDecodingTestCase.class, "mime2.txt")); TestPartHandler handler = new TestPartHandler(); MultipartParser.ParseState parser = MultipartParser.beginParse(DefaultServer.getBufferPool(), handler, "unique-boundary-1".getBytes(), "ISO-8859-1"); ByteBuffer buf = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)); parser.parse(buf); Assert.assertTrue(parser.isComplete()); Assert.assertEquals(2, handler.parts.size()); Assert.assertEquals("Here is some text.", handler.parts.get(0).data.toString()); Assert.assertEquals("Here is some more text.", handler.parts.get(1).data.toString()); Assert.assertEquals("text/plain", handler.parts.get(0).map.getFirst(Headers.CONTENT_TYPE)); } @Test public void testBase64MimeDecoding() throws IOException { final String data = fixLineEndings(FileUtils.readFile(MimeDecodingTestCase.class, "mime3.txt")); TestPartHandler handler = new TestPartHandler(); MultipartParser.ParseState parser = MultipartParser.beginParse(DefaultServer.getBufferPool(), handler, "unique-boundary-1".getBytes(), "ISO-8859-1"); ByteBuffer buf = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)); parser.parse(buf); Assert.assertTrue(parser.isComplete()); Assert.assertEquals(2, handler.parts.size()); Assert.assertEquals("This is some base64 text.", handler.parts.get(0).data.toString()); Assert.assertEquals("This is some more base64 text.", handler.parts.get(1).data.toString()); Assert.assertEquals("text/plain", handler.parts.get(0).map.getFirst(Headers.CONTENT_TYPE)); } @Test public void testBase64MimeDecodingWithSmallBuffers() throws IOException { final String data = fixLineEndings(FileUtils.readFile(MimeDecodingTestCase.class, "mime3.txt")); TestPartHandler handler = new TestPartHandler(); MultipartParser.ParseState parser = MultipartParser.beginParse(new DefaultByteBufferPool(true, 6, 100, 0), handler, "unique-boundary-1".getBytes(), "ISO-8859-1"); ByteBuffer buf = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)); parser.parse(buf); Assert.assertTrue(parser.isComplete()); Assert.assertEquals(2, handler.parts.size()); Assert.assertEquals("This is some base64 text.", handler.parts.get(0).data.toString()); Assert.assertEquals("This is some more base64 text.", handler.parts.get(1).data.toString()); Assert.assertEquals("text/plain", handler.parts.get(0).map.getFirst(Headers.CONTENT_TYPE)); } @Test public void testQuotedPrintable() throws IOException { final String data = fixLineEndings(FileUtils.readFile(MimeDecodingTestCase.class, "mime4.txt")); TestPartHandler handler = new TestPartHandler(); MultipartParser.ParseState parser = MultipartParser.beginParse(DefaultServer.getBufferPool(), handler, "someboundarytext".getBytes(), "ISO-8859-1"); ByteBuffer buf = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)); parser.parse(buf); Assert.assertTrue(parser.isComplete()); Assert.assertEquals(1, handler.parts.size()); Assert.assertEquals("time=money.", handler.parts.get(0).data.toString()); Assert.assertEquals("text/plain", handler.parts.get(0).map.getFirst(Headers.CONTENT_TYPE)); } private static class TestPartHandler implements MultipartParser.PartHandler { private final List<Part> parts = new ArrayList<>(); private Part current; @Override public void beginPart(final HeaderMap headers) { current = new Part(headers); parts.add(current); } @Override public void data(final ByteBuffer buffer) { while (buffer.hasRemaining()) { current.data.append((char) buffer.get()); } } @Override public void endPart() { } } private static class Part { private final HeaderMap map; private final StringBuilder data = new StringBuilder(); private Part(final HeaderMap map) { this.map = map; } } private static String fixLineEndings(final String string) { final StringBuilder builder = new StringBuilder(); for(int i = 0; i < string.length(); ++i) { char c = string.charAt(i); if(c == '\n') { if(i == 0 || string.charAt(i-1) != '\r') { builder.append("\r\n"); } else { builder.append('\n'); } } else if(c == '\r') { if(i+1 == string.length() || string.charAt(i+1) != '\n') { builder.append("\r\n"); } else { builder.append('\r'); } } else { builder.append(c); } } return builder.toString(); } }