1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.fileupload2.core;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.nio.file.InvalidPathException;
22
23 import org.apache.commons.io.input.BoundedInputStream;
24
25 /**
26 * Default implementation of {@link FileItemInput}.
27 */
28 class FileItemInputImpl implements FileItemInput {
29
30 /**
31 * The File Item iterator implementation.
32 *
33 * @see FileItemInputIteratorImpl
34 */
35 private final FileItemInputIteratorImpl fileItemInputIteratorImpl;
36
37 /**
38 * The file items content type.
39 */
40 private final String contentType;
41
42 /**
43 * The file items field name.
44 */
45 private final String fieldName;
46
47 /**
48 * The file items file name.
49 */
50 private final String fileName;
51
52 /**
53 * Whether the file item is a form field.
54 */
55 private final boolean formField;
56
57 /**
58 * The file items input stream.
59 */
60 private final InputStream inputStream;
61
62 /**
63 * The file items input stream closed flag.
64 */
65 private boolean inputStreamClosed;
66
67 /**
68 * The headers, if any.
69 */
70 private FileItemHeaders headers;
71
72 /**
73 * Creates a new instance.
74 *
75 * @param fileItemIterator The {@link FileItemInputIteratorImpl iterator}, which returned this file item.
76 * @param fileName The items file name, or null.
77 * @param fieldName The items field name.
78 * @param contentType The items content type, or null.
79 * @param formField Whether the item is a form field.
80 * @param contentLength The items content length, if known, or -1
81 * @throws IOException Creating the file item failed.
82 * @throws FileUploadException Parsing the incoming data stream failed.
83 */
84 FileItemInputImpl(final FileItemInputIteratorImpl fileItemIterator, final String fileName, final String fieldName, final String contentType,
85 final boolean formField, final long contentLength) throws FileUploadException, IOException {
86 this.fileItemInputIteratorImpl = fileItemIterator;
87 this.fileName = fileName;
88 this.fieldName = fieldName;
89 this.contentType = contentType;
90 this.formField = formField;
91 final var fileSizeMax = fileItemInputIteratorImpl.getFileSizeMax();
92 if (fileSizeMax != -1 && contentLength != -1 && contentLength > fileSizeMax) {
93 throw new FileUploadByteCountLimitException(String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, fileSizeMax),
94 contentLength, fileSizeMax, fileName, fieldName);
95 }
96 // OK to construct stream now
97 final var itemInputStream = fileItemInputIteratorImpl.getMultiPartInput().newInputStream();
98 InputStream istream = itemInputStream;
99 if (fileSizeMax != -1) {
100 // onMaxLength will be called when the length is greater than _or equal to_ the supplied maxLength.
101 // Because we only want to throw an exception when the length is greater than fileSizeMax, we
102 // increment fileSizeMax by 1.
103 istream = new BoundedInputStream(istream, fileSizeMax + 1) {
104 @Override
105 protected void onMaxLength(final long sizeMax, final long count) throws IOException {
106 itemInputStream.close(true);
107 throw new FileUploadByteCountLimitException(
108 String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, fileSizeMax), count, fileSizeMax, fileName,
109 fieldName);
110 }
111 };
112 }
113 this.inputStream = istream;
114 }
115
116 /**
117 * Closes the file item.
118 *
119 * @throws IOException An I/O error occurred.
120 */
121 public void close() throws IOException {
122 inputStream.close();
123 inputStreamClosed = true;
124 }
125
126 /**
127 * Gets the content type, or null.
128 *
129 * @return Content type, if known, or null.
130 */
131 @Override
132 public String getContentType() {
133 return contentType;
134 }
135
136 /**
137 * Gets the items field name.
138 *
139 * @return Field name.
140 */
141 @Override
142 public String getFieldName() {
143 return fieldName;
144 }
145
146 /**
147 * Gets the headers.
148 *
149 * @return The items header object
150 */
151 @Override
152 public FileItemHeaders getHeaders() {
153 return headers;
154 }
155
156 /**
157 * Gets the input stream, which may be used to read the items contents.
158 *
159 * @return Opened input stream.
160 * @throws IOException An I/O error occurred.
161 */
162 @Override
163 public InputStream getInputStream() throws IOException {
164 if (inputStreamClosed) {
165 throw new FileItemInput.ItemSkippedException("getInputStream()");
166 }
167 return inputStream;
168 }
169
170 /**
171 * Gets the file name.
172 *
173 * @return File name, if known, or null.
174 * @throws InvalidPathException The file name is invalid, for example it contains a NUL character, which might be an indicator of a security attack. If you
175 * intend to use the file name anyways, catch the exception and use InvalidPathException#getInput().
176 */
177 @Override
178 public String getName() {
179 return DiskFileItem.checkFileName(fileName);
180 }
181
182 /**
183 * Tests whether this is a form field.
184 *
185 * @return True, if the item is a form field, otherwise false.
186 */
187 @Override
188 public boolean isFormField() {
189 return formField;
190 }
191
192 /**
193 * Sets the file item headers.
194 *
195 * @param headers The items header object
196 */
197 @Override
198 public FileItemInputImpl setHeaders(final FileItemHeaders headers) {
199 this.headers = headers;
200 return this;
201 }
202
203 }