001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.fileupload2.core;
018
019import static org.junit.jupiter.api.Assertions.assertEquals;
020import static org.junit.jupiter.api.Assertions.assertFalse;
021import static org.junit.jupiter.api.Assertions.assertNull;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023
024import java.io.IOException;
025import java.nio.charset.StandardCharsets;
026
027import org.junit.jupiter.api.Test;
028
029/**
030 * Common tests for implementations of {@link AbstractFileUpload}. This is a parameterized test. Tests must be valid and common to all implementations of
031 * FileUpload added as parameter in this class.
032 *
033 * @param <AFU> The {@link AbstractFileUpload} type.
034 * @param <R>   The FileUpload request type.
035 * @param <I>   The FileItem type.
036 * @param <F>   The FileItemFactory type.
037 */
038public abstract class AbstractFileUploadTest<AFU extends AbstractFileUpload<R, I, F>, R, I extends FileItem<I>, F extends FileItemFactory<I>>
039        extends AbstractFileUploadWrapper<AFU, R, I, F> {
040
041    protected AbstractFileUploadTest(final AFU fileUpload) {
042        super(fileUpload);
043    }
044
045    private void assertHeaders(final String[] headerNames, final String[] headerValues, final I fileItems, final int index) {
046        for (var i = 0; i < headerNames.length; i++) {
047            final var value = fileItems.getHeaders().getHeader(headerNames[i]);
048            if (i == index) {
049                assertEquals(headerValues[i], value);
050            } else {
051                assertNull(value);
052            }
053        }
054    }
055
056    /**
057     * Tests <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-239">FILEUPLOAD-239</a>
058     *
059     * @throws IOException Test failure.
060     */
061    @Test
062    public void testContentTypeAttachment() throws IOException {
063        // @formatter:off
064        final var fileItems = parseUpload(upload,
065                "-----1234\r\n" +
066                "content-disposition: form-data; name=\"field1\"\r\n" +
067                "\r\n" +
068                "Joe Blow\r\n" +
069                "-----1234\r\n" +
070                "content-disposition: form-data; name=\"pics\"\r\n" +
071                "Content-type: multipart/mixed, boundary=---9876\r\n" +
072                "\r\n" +
073                "-----9876\r\n" +
074                "Content-disposition: attachment; filename=\"file1.txt\"\r\n" +
075                "Content-Type: text/plain\r\n" +
076                "\r\n" +
077                "... contents of file1.txt ...\r\n" +
078                "-----9876--\r\n" +
079                "-----1234--\r\n");
080        // @formatter:on
081        assertEquals(2, fileItems.size());
082
083        final var field = fileItems.get(0);
084        assertEquals("field1", field.getFieldName());
085        assertTrue(field.isFormField());
086        assertEquals("Joe Blow", field.getString());
087
088        final var fileItem = fileItems.get(1);
089        assertEquals("pics", fileItem.getFieldName());
090        assertFalse(fileItem.isFormField());
091        assertEquals("... contents of file1.txt ...", fileItem.getString());
092        assertEquals("text/plain", fileItem.getContentType());
093        assertEquals("file1.txt", fileItem.getName());
094    }
095
096    /**
097     * This is what the browser does if you submit the form without choosing a file.
098     *
099     * @throws FileUploadException Test failure.
100     */
101    @Test
102    public void testEmptyFile() throws FileUploadException {
103        // @formatter:off
104        final var fileItems = parseUpload (upload,
105                                                "-----1234\r\n" +
106                                                "Content-Disposition: form-data; name=\"file\"; filename=\"\"\r\n" +
107                                                "\r\n" +
108                                                "\r\n" +
109                                                "-----1234--\r\n");
110        // @formatter:on
111        assertEquals(1, fileItems.size());
112
113        final var file = fileItems.get(0);
114        assertFalse(file.isFormField());
115        assertEquals("", file.getString());
116        assertEquals("", file.getName());
117    }
118
119    @Test
120    public void testFileNameCaseSensitivity() throws IOException {
121        // @formatter:off
122        final var fileItems = parseUpload(upload,
123                                               "-----1234\r\n" +
124                                               "Content-Disposition: form-data; "
125                                             + "name=\"FiLe\"; filename=\"FOO.tab\"\r\n" +
126                                               "Content-Type: text/whatever\r\n" +
127                                               "\r\n" +
128                                               "This is the content of the file\n" +
129                                               "\r\n" +
130                                               "-----1234--\r\n");
131        // @formatter:on
132        assertEquals(1, fileItems.size());
133
134        final var file = fileItems.get(0);
135        assertEquals("FiLe", file.getFieldName());
136        assertEquals("FOO.tab", file.getName());
137    }
138
139    @Test
140    public void testFileUpload() throws IOException {
141        // @formatter:off
142        final var fileItems = parseUpload(upload,
143                                               "-----1234\r\n" +
144                                               "Content-Disposition: "
145                                               + "form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
146                                               "Content-Type: text/whatever\r\n" +
147                                               "\r\n" +
148                                               "This is the content of the file\n" +
149                                               "\r\n" +
150                                               "-----1234\r\n" +
151                                               "Content-Disposition: form-data; name=\"field\"\r\n" +
152                                               "\r\n" +
153                                               "fieldValue\r\n" +
154                                               "-----1234\r\n" +
155                                               "Content-Disposition: form-data; name=\"multi\"\r\n" +
156                                               "\r\n" +
157                                               "value1\r\n" +
158                                               "-----1234\r\n" +
159                                               "Content-Disposition: form-data; name=\"multi\"\r\n" +
160                                               "\r\n" +
161                                               "value2\r\n" +
162                                               "-----1234--\r\n");
163        // @formatter:on
164        assertEquals(4, fileItems.size());
165
166        final var file = fileItems.get(0);
167        assertEquals("file", file.getFieldName());
168        assertFalse(file.isFormField());
169        assertEquals("This is the content of the file\n", file.getString());
170        assertEquals("text/whatever", file.getContentType());
171        assertEquals("foo.tab", file.getName());
172
173        final var field = fileItems.get(1);
174        assertEquals("field", field.getFieldName());
175        assertTrue(field.isFormField());
176        assertEquals("fieldValue", field.getString());
177
178        final var multi0 = fileItems.get(2);
179        assertEquals("multi", multi0.getFieldName());
180        assertTrue(multi0.isFormField());
181        assertEquals("value1", multi0.getString());
182
183        final var multi1 = fileItems.get(3);
184        assertEquals("multi", multi1.getFieldName());
185        assertTrue(multi1.isFormField());
186        assertEquals("value2", multi1.getString());
187    }
188
189    /**
190     * Test case for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-130">FILEUPLOAD-130</a>.
191     *
192     * @throws IOException Test failure.
193     */
194    @Test
195    public void testFileUpload130() throws IOException {
196        final String[] headerNames = { "SomeHeader", "OtherHeader", "YetAnotherHeader", "WhatAHeader" };
197        final String[] headerValues = { "present", "Is there", "Here", "Is That" };
198        // @formatter:off
199        final var fileItems = parseUpload(upload,
200                                               "-----1234\r\n" +
201                                               "Content-Disposition: form-data; name=\"file\"; "
202                                             + "filename=\"foo.tab\"\r\n" +
203                                               "Content-Type: text/whatever\r\n" +
204                                               headerNames[0] + ": " + headerValues[0] + "\r\n" +
205                                               "\r\n" +
206                                               "This is the content of the file\n" +
207                                               "\r\n" +
208                                               "-----1234\r\n" +
209                                               "Content-Disposition: form-data; \r\n" +
210                                               "\tname=\"field\"\r\n" +
211                                               headerNames[1] + ": " + headerValues[1] + "\r\n" +
212                                               "\r\n" +
213                                               "fieldValue\r\n" +
214                                               "-----1234\r\n" +
215                                               "Content-Disposition: form-data;\r\n" +
216                                               "     name=\"multi\"\r\n" +
217                                               headerNames[2] + ": " + headerValues[2] + "\r\n" +
218                                               "\r\n" +
219                                               "value1\r\n" +
220                                               "-----1234\r\n" +
221                                               "Content-Disposition: form-data; name=\"multi\"\r\n" +
222                                               headerNames[3] + ": " + headerValues[3] + "\r\n" +
223                                               "\r\n" +
224                                               "value2\r\n" +
225                                               "-----1234--\r\n");
226        // @formatter:on
227        assertEquals(4, fileItems.size());
228
229        final var file = fileItems.get(0);
230        assertHeaders(headerNames, headerValues, file, 0);
231
232        final var field = fileItems.get(1);
233        assertHeaders(headerNames, headerValues, field, 1);
234
235        final var multi0 = fileItems.get(2);
236        assertHeaders(headerNames, headerValues, multi0, 2);
237
238        final var multi1 = fileItems.get(3);
239        assertHeaders(headerNames, headerValues, multi1, 3);
240    }
241
242    /**
243     * Test for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-62">FILEUPLOAD-62</a>
244     *
245     * @throws IOException Test failure.
246     */
247    @Test
248    public void testFILEUPLOAD62() throws IOException {
249        // @formatter:off
250        final var contentType = "multipart/form-data; boundary=AaB03x";
251        final var request =
252            "--AaB03x\r\n" +
253            "content-disposition: form-data; name=\"field1\"\r\n" +
254            "\r\n" +
255            "Joe Blow\r\n" +
256            "--AaB03x\r\n" +
257            "content-disposition: form-data; name=\"pics\"\r\n" +
258            "Content-type: multipart/mixed; boundary=BbC04y\r\n" +
259            "\r\n" +
260            "--BbC04y\r\n" +
261            "Content-disposition: attachment; filename=\"file1.txt\"\r\n" +
262            "Content-Type: text/plain\r\n" +
263            "\r\n" +
264            "... contents of file1.txt ...\r\n" +
265            "--BbC04y\r\n" +
266            "Content-disposition: attachment; filename=\"file2.gif\"\r\n" +
267            "Content-type: image/gif\r\n" +
268            "Content-Transfer-Encoding: binary\r\n" +
269            "\r\n" +
270            "...contents of file2.gif...\r\n" +
271            "--BbC04y--\r\n" +
272            "--AaB03x--";
273        // @formatter:on
274        final var fileItems = parseUpload(upload, request.getBytes(StandardCharsets.US_ASCII), contentType);
275        assertEquals(3, fileItems.size());
276        final var item0 = fileItems.get(0);
277        assertEquals("field1", item0.getFieldName());
278        assertNull(item0.getName());
279        assertEquals("Joe Blow", new String(item0.get()));
280        final var item1 = fileItems.get(1);
281        assertEquals("pics", item1.getFieldName());
282        assertEquals("file1.txt", item1.getName());
283        assertEquals("... contents of file1.txt ...", new String(item1.get()));
284        final var item2 = fileItems.get(2);
285        assertEquals("pics", item2.getFieldName());
286        assertEquals("file2.gif", item2.getName());
287        assertEquals("...contents of file2.gif...", new String(item2.get()));
288    }
289
290    /**
291     * Test for <a href="https://issues.apache.org/jira/browse/FILEUPLOAD-111">FILEUPLOAD-111</a>
292     *
293     * @throws IOException Test failure.
294     */
295    @Test
296    public void testFoldedHeaders() throws IOException {
297        // @formatter:off
298        final var fileItems = parseUpload(upload, "-----1234\r\n" +
299                "Content-Disposition: form-data; name=\"file\"; filename=\"foo.tab\"\r\n" +
300                "Content-Type: text/whatever\r\n" +
301                "\r\n" +
302                "This is the content of the file\n" +
303                "\r\n" +
304                "-----1234\r\n" +
305                "Content-Disposition: form-data; \r\n" +
306                "\tname=\"field\"\r\n" +
307                "\r\n" +
308                "fieldValue\r\n" +
309                "-----1234\r\n" +
310                "Content-Disposition: form-data;\r\n" +
311                "     name=\"multi\"\r\n" +
312                "\r\n" +
313                "value1\r\n" +
314                "-----1234\r\n" +
315                "Content-Disposition: form-data; name=\"multi\"\r\n" +
316                "\r\n" +
317                "value2\r\n" +
318                "-----1234--\r\n");
319        // @formatter:on
320        assertEquals(4, fileItems.size());
321
322        final var file = fileItems.get(0);
323        assertEquals("file", file.getFieldName());
324        assertFalse(file.isFormField());
325        assertEquals("This is the content of the file\n", file.getString());
326        assertEquals("text/whatever", file.getContentType());
327        assertEquals("foo.tab", file.getName());
328
329        final var field = fileItems.get(1);
330        assertEquals("field", field.getFieldName());
331        assertTrue(field.isFormField());
332        assertEquals("fieldValue", field.getString());
333
334        final var multi0 = fileItems.get(2);
335        assertEquals("multi", multi0.getFieldName());
336        assertTrue(multi0.isFormField());
337        assertEquals("value1", multi0.getString());
338
339        final var multi1 = fileItems.get(3);
340        assertEquals("multi", multi1.getFieldName());
341        assertTrue(multi1.isFormField());
342        assertEquals("value2", multi1.getString());
343    }
344
345    /**
346     * Internet Explorer 5 for the Mac has a bug where the carriage return is missing on any boundary line immediately preceding an input with type=image.
347     * (type=submit does not have the bug.)
348     *
349     * @throws FileUploadException Test failure.
350     */
351    @Test
352    public void testIE5MacBug() throws FileUploadException {
353        final var fileItems = parseUpload(upload,
354        // @formatter:off
355                "-----1234\r\n" +
356                "Content-Disposition: form-data; name=\"field1\"\r\n" +
357                "\r\n" +
358                "fieldValue\r\n" +
359                "-----1234\n" + // NOTE \r missing
360                "Content-Disposition: form-data; name=\"submitName.x\"\r\n" +
361                "\r\n" +
362                "42\r\n" +
363                "-----1234\n" + // NOTE \r missing
364                "Content-Disposition: form-data; name=\"submitName.y\"\r\n" +
365                "\r\n" +
366                "21\r\n" +
367                "-----1234\r\n" +
368                "Content-Disposition: form-data; name=\"field2\"\r\n" +
369                "\r\n" +
370                "fieldValue2\r\n" +
371                "-----1234--\r\n");
372        //@formatter:on
373
374        assertEquals(4, fileItems.size());
375
376        final var field1 = fileItems.get(0);
377        assertEquals("field1", field1.getFieldName());
378        assertTrue(field1.isFormField());
379        assertEquals("fieldValue", field1.getString());
380
381        final var submitX = fileItems.get(1);
382        assertEquals("submitName.x", submitX.getFieldName());
383        assertTrue(submitX.isFormField());
384        assertEquals("42", submitX.getString());
385
386        final var submitY = fileItems.get(2);
387        assertEquals("submitName.y", submitY.getFieldName());
388        assertTrue(submitY.isFormField());
389        assertEquals("21", submitY.getString());
390
391        final var field2 = fileItems.get(3);
392        assertEquals("field2", field2.getFieldName());
393        assertTrue(field2.isFormField());
394        assertEquals("fieldValue2", field2.getString());
395    }
396}