| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| MultipartStream | 
 | 
 | 3.210526315789474;3.211 | ||||
| MultipartStream$IllegalBoundaryException | 
 | 
 | 3.210526315789474;3.211 | ||||
| MultipartStream$ItemInputStream | 
 | 
 | 3.210526315789474;3.211 | ||||
| MultipartStream$MalformedStreamException | 
 | 
 | 3.210526315789474;3.211 | ||||
| MultipartStream$ProgressNotifier | 
 | 
 | 3.210526315789474;3.211 | 
| 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.fileupload; | |
| 18 | ||
| 19 | import static java.lang.String.format; | |
| 20 | ||
| 21 |  import java.io.ByteArrayOutputStream; | |
| 22 |  import java.io.IOException; | |
| 23 |  import java.io.InputStream; | |
| 24 |  import java.io.OutputStream; | |
| 25 |  import java.io.UnsupportedEncodingException; | |
| 26 | ||
| 27 |  import org.apache.commons.fileupload.FileUploadBase.FileUploadIOException; | |
| 28 |  import org.apache.commons.fileupload.util.Closeable; | |
| 29 |  import org.apache.commons.fileupload.util.Streams; | |
| 30 | ||
| 31 |  /** | |
| 32 |   * <p> Low level API for processing file uploads. | |
| 33 |   * | |
| 34 |   * <p> This class can be used to process data streams conforming to MIME | |
| 35 |   * 'multipart' format as defined in | |
| 36 |   * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily | |
| 37 |   * large amounts of data in the stream can be processed under constant | |
| 38 |   * memory usage. | |
| 39 |   * | |
| 40 |   * <p> The format of the stream is defined in the following way:<br> | |
| 41 |   * | |
| 42 |   * <code> | |
| 43 |   *   multipart-body := preamble 1*encapsulation close-delimiter epilogue<br> | |
| 44 |   *   encapsulation := delimiter body CRLF<br> | |
| 45 |   *   delimiter := "--" boundary CRLF<br> | |
| 46 |   *   close-delimiter := "--" boundary "--"<br> | |
| 47 |   *   preamble := <ignore><br> | |
| 48 |   *   epilogue := <ignore><br> | |
| 49 |   *   body := header-part CRLF body-part<br> | |
| 50 |   *   header-part := 1*header CRLF<br> | |
| 51 |   *   header := header-name ":" header-value<br> | |
| 52 |   *   header-name := <printable ascii characters except ":"><br> | |
| 53 |   *   header-value := <any ascii characters except CR & LF><br> | |
| 54 |   *   body-data := <arbitrary data><br> | |
| 55 |   * </code> | |
| 56 |   * | |
| 57 |   * <p>Note that body-data can contain another mulipart entity.  There | |
| 58 |   * is limited support for single pass processing of such nested | |
| 59 |   * streams.  The nested stream is <strong>required</strong> to have a | |
| 60 |   * boundary token of the same length as the parent stream (see {@link | |
| 61 |   * #setBoundary(byte[])}). | |
| 62 |   * | |
| 63 |   * <p>Here is an example of usage of this class.<br> | |
| 64 |   * | |
| 65 |   * <pre> | |
| 66 |   *   try { | |
| 67 |   *     MultipartStream multipartStream = new MultipartStream(input, boundary); | |
| 68 |   *     boolean nextPart = multipartStream.skipPreamble(); | |
| 69 |   *     OutputStream output; | |
| 70 |   *     while(nextPart) { | |
| 71 |   *       String header = multipartStream.readHeaders(); | |
| 72 |   *       // process headers | |
| 73 |   *       // create some output stream | |
| 74 |   *       multipartStream.readBodyData(output); | |
| 75 |   *       nextPart = multipartStream.readBoundary(); | |
| 76 |   *     } | |
| 77 |   *   } catch(MultipartStream.MalformedStreamException e) { | |
| 78 |   *     // the stream failed to follow required syntax | |
| 79 |   *   } catch(IOException e) { | |
| 80 |   *     // a read or write error occurred | |
| 81 |   *   } | |
| 82 |   * </pre> | |
| 83 |   */ | |
| 84 | 66303156 | public class MultipartStream { | 
| 85 | ||
| 86 |      /** | |
| 87 |       * Internal class, which is used to invoke the | |
| 88 |       * {@link ProgressListener}. | |
| 89 |       */ | |
| 90 | public static class ProgressNotifier { | |
| 91 | ||
| 92 |          /** | |
| 93 |           * The listener to invoke. | |
| 94 |           */ | |
| 95 | private final ProgressListener listener; | |
| 96 | ||
| 97 |          /** | |
| 98 |           * Number of expected bytes, if known, or -1. | |
| 99 |           */ | |
| 100 | private final long contentLength; | |
| 101 | ||
| 102 |          /** | |
| 103 |           * Number of bytes, which have been read so far. | |
| 104 |           */ | |
| 105 | private long bytesRead; | |
| 106 | ||
| 107 |          /** | |
| 108 |           * Number of items, which have been read so far. | |
| 109 |           */ | |
| 110 | private int items; | |
| 111 | ||
| 112 |          /** | |
| 113 |           * Creates a new instance with the given listener | |
| 114 |           * and content length. | |
| 115 |           * | |
| 116 |           * @param pListener The listener to invoke. | |
| 117 |           * @param pContentLength The expected content length. | |
| 118 |           */ | |
| 119 | 39 |          ProgressNotifier(ProgressListener pListener, long pContentLength) { | 
| 120 | 39 | listener = pListener; | 
| 121 | 39 | contentLength = pContentLength; | 
| 122 | 39 | } | 
| 123 | ||
| 124 |          /** | |
| 125 |           * Called to indicate that bytes have been read. | |
| 126 |           * | |
| 127 |           * @param pBytes Number of bytes, which have been read. | |
| 128 |           */ | |
| 129 | void noteBytesRead(int pBytes) { | |
| 130 |              /* Indicates, that the given number of bytes have been read from | |
| 131 |               * the input stream. | |
| 132 |               */ | |
| 133 | 8507 | bytesRead += pBytes; | 
| 134 | 8507 | notifyListener(); | 
| 135 | 8507 | } | 
| 136 | ||
| 137 |          /** | |
| 138 |           * Called to indicate, that a new file item has been detected. | |
| 139 |           */ | |
| 140 |          void noteItem() { | |
| 141 | 3190 | ++items; | 
| 142 | 3190 | notifyListener(); | 
| 143 | 3190 | } | 
| 144 | ||
| 145 |          /** | |
| 146 |           * Called for notifying the listener. | |
| 147 |           */ | |
| 148 | private void notifyListener() { | |
| 149 | 11697 | if (listener != null) { | 
| 150 | 5212 | listener.update(bytesRead, contentLength, items); | 
| 151 | } | |
| 152 | 11697 | } | 
| 153 | ||
| 154 | } | |
| 155 | ||
| 156 |      // ----------------------------------------------------- Manifest constants | |
| 157 | ||
| 158 |      /** | |
| 159 |       * The Carriage Return ASCII character value. | |
| 160 |       */ | |
| 161 | public static final byte CR = 0x0D; | |
| 162 | ||
| 163 |      /** | |
| 164 |       * The Line Feed ASCII character value. | |
| 165 |       */ | |
| 166 | public static final byte LF = 0x0A; | |
| 167 | ||
| 168 |      /** | |
| 169 |       * The dash (-) ASCII character value. | |
| 170 |       */ | |
| 171 | public static final byte DASH = 0x2D; | |
| 172 | ||
| 173 |      /** | |
| 174 |       * The maximum length of <code>header-part</code> that will be | |
| 175 |       * processed (10 kilobytes = 10240 bytes.). | |
| 176 |       */ | |
| 177 | public static final int HEADER_PART_SIZE_MAX = 10240; | |
| 178 | ||
| 179 |      /** | |
| 180 |       * The default length of the buffer used for processing a request. | |
| 181 |       */ | |
| 182 | protected static final int DEFAULT_BUFSIZE = 4096; | |
| 183 | ||
| 184 |      /** | |
| 185 |       * A byte sequence that marks the end of <code>header-part</code> | |
| 186 |       * (<code>CRLFCRLF</code>). | |
| 187 |       */ | |
| 188 | 1 | protected static final byte[] HEADER_SEPARATOR = {CR, LF, CR, LF}; | 
| 189 | ||
| 190 |      /** | |
| 191 |       * A byte sequence that that follows a delimiter that will be | |
| 192 |       * followed by an encapsulation (<code>CRLF</code>). | |
| 193 |       */ | |
| 194 | 1 | protected static final byte[] FIELD_SEPARATOR = {CR, LF}; | 
| 195 | ||
| 196 |      /** | |
| 197 |       * A byte sequence that that follows a delimiter of the last | |
| 198 |       * encapsulation in the stream (<code>--</code>). | |
| 199 |       */ | |
| 200 | 1 | protected static final byte[] STREAM_TERMINATOR = {DASH, DASH}; | 
| 201 | ||
| 202 |      /** | |
| 203 |       * A byte sequence that precedes a boundary (<code>CRLF--</code>). | |
| 204 |       */ | |
| 205 | 1 | protected static final byte[] BOUNDARY_PREFIX = {CR, LF, DASH, DASH}; | 
| 206 | ||
| 207 |      // ----------------------------------------------------------- Data members | |
| 208 | ||
| 209 |      /** | |
| 210 |       * The input stream from which data is read. | |
| 211 |       */ | |
| 212 | private final InputStream input; | |
| 213 | ||
| 214 |      /** | |
| 215 |       * The length of the boundary token plus the leading <code>CRLF--</code>. | |
| 216 |       */ | |
| 217 | private int boundaryLength; | |
| 218 | ||
| 219 |      /** | |
| 220 |       * The amount of data, in bytes, that must be kept in the buffer in order | |
| 221 |       * to detect delimiters reliably. | |
| 222 |       */ | |
| 223 | private final int keepRegion; | |
| 224 | ||
| 225 |      /** | |
| 226 |       * The byte sequence that partitions the stream. | |
| 227 |       */ | |
| 228 | private final byte[] boundary; | |
| 229 | ||
| 230 |      /** | |
| 231 |       * The table for Knuth-Morris-Pratt search algorithm. | |
| 232 |       */ | |
| 233 | private final int[] boundaryTable; | |
| 234 | ||
| 235 |      /** | |
| 236 |       * The length of the buffer used for processing the request. | |
| 237 |       */ | |
| 238 | private final int bufSize; | |
| 239 | ||
| 240 |      /** | |
| 241 |       * The buffer used for processing the request. | |
| 242 |       */ | |
| 243 | private final byte[] buffer; | |
| 244 | ||
| 245 |      /** | |
| 246 |       * The index of first valid character in the buffer. | |
| 247 |       * <br> | |
| 248 |       * 0 <= head < bufSize | |
| 249 |       */ | |
| 250 | private int head; | |
| 251 | ||
| 252 |      /** | |
| 253 |       * The index of last valid character in the buffer + 1. | |
| 254 |       * <br> | |
| 255 |       * 0 <= tail <= bufSize | |
| 256 |       */ | |
| 257 | private int tail; | |
| 258 | ||
| 259 |      /** | |
| 260 |       * The content encoding to use when reading headers. | |
| 261 |       */ | |
| 262 |      private String headerEncoding; | |
| 263 | ||
| 264 |      /** | |
| 265 |       * The progress notifier, if any, or null. | |
| 266 |       */ | |
| 267 | private final ProgressNotifier notifier; | |
| 268 | ||
| 269 |      // ----------------------------------------------------------- Constructors | |
| 270 | ||
| 271 |      /** | |
| 272 |       * Creates a new instance. | |
| 273 |       * | |
| 274 |       * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int, | |
| 275 |       * ProgressNotifier)} | |
| 276 |       */ | |
| 277 | @Deprecated | |
| 278 |      public MultipartStream() { | |
| 279 | 0 |          this(null, null, null); | 
| 280 | 0 |      } | 
| 281 | ||
| 282 |      /** | |
| 283 |       * <p> Constructs a <code>MultipartStream</code> with a custom size buffer | |
| 284 |       * and no progress notifier. | |
| 285 |       * | |
| 286 |       * <p> Note that the buffer must be at least big enough to contain the | |
| 287 |       * boundary string, plus 4 characters for CR/LF and double dash, plus at | |
| 288 |       * least one byte of data.  Too small a buffer size setting will degrade | |
| 289 |       * performance. | |
| 290 |       * | |
| 291 |       * @param input    The <code>InputStream</code> to serve as a data source. | |
| 292 |       * @param boundary The token used for dividing the stream into | |
| 293 |       *                 <code>encapsulations</code>. | |
| 294 |       * @param bufSize  The size of the buffer to be used, in bytes. | |
| 295 |       * | |
| 296 |       * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int, | |
| 297 |       * ProgressNotifier)}. | |
| 298 |       */ | |
| 299 | @Deprecated | |
| 300 | public MultipartStream(InputStream input, byte[] boundary, int bufSize) { | |
| 301 | 0 |          this(input, boundary, bufSize, null); | 
| 302 | 0 |      } | 
| 303 | ||
| 304 |      /** | |
| 305 |       * <p> Constructs a <code>MultipartStream</code> with a custom size buffer. | |
| 306 |       * | |
| 307 |       * <p> Note that the buffer must be at least big enough to contain the | |
| 308 |       * boundary string, plus 4 characters for CR/LF and double dash, plus at | |
| 309 |       * least one byte of data.  Too small a buffer size setting will degrade | |
| 310 |       * performance. | |
| 311 |       * | |
| 312 |       * @param input    The <code>InputStream</code> to serve as a data source. | |
| 313 |       * @param boundary The token used for dividing the stream into | |
| 314 |       *                 <code>encapsulations</code>. | |
| 315 |       * @param bufSize  The size of the buffer to be used, in bytes. | |
| 316 |       * @param pNotifier The notifier, which is used for calling the | |
| 317 |       *                  progress listener, if any. | |
| 318 |       * | |
| 319 |       * @throws IllegalArgumentException If the buffer size is too small | |
| 320 |       * | |
| 321 |       * @since 1.3.1 | |
| 322 |       */ | |
| 323 |      public MultipartStream(InputStream input, | |
| 324 |              byte[] boundary, | |
| 325 |              int bufSize, | |
| 326 | 39 |              ProgressNotifier pNotifier) { | 
| 327 | ||
| 328 | 39 |          if (boundary == null) { | 
| 329 | 0 |              throw new IllegalArgumentException("boundary may not be null"); | 
| 330 | } | |
| 331 |          // We prepend CR/LF to the boundary to chop trailing CR/LF from | |
| 332 |          // body-data tokens. | |
| 333 | 39 |          this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length; | 
| 334 | 39 | if (bufSize < this.boundaryLength + 1) { | 
| 335 | 1 | throw new IllegalArgumentException( | 
| 336 |                      "The buffer size specified for the MultipartStream is too small"); | |
| 337 | } | |
| 338 | ||
| 339 | 38 |          this.input = input; | 
| 340 | 38 |          this.bufSize = Math.max(bufSize, boundaryLength * 2); | 
| 341 | 38 | this.buffer = new byte[this.bufSize]; | 
| 342 | 38 |          this.notifier = pNotifier; | 
| 343 | ||
| 344 | 38 | this.boundary = new byte[this.boundaryLength]; | 
| 345 | 38 | this.boundaryTable = new int[this.boundaryLength + 1]; | 
| 346 | 38 | this.keepRegion = this.boundary.length; | 
| 347 | ||
| 348 | 38 |          System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, | 
| 349 | BOUNDARY_PREFIX.length); | |
| 350 | 38 |          System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, | 
| 351 | boundary.length); | |
| 352 | 38 | computeBoundaryTable(); | 
| 353 | ||
| 354 | 38 | head = 0; | 
| 355 | 38 | tail = 0; | 
| 356 | 38 | } | 
| 357 | ||
| 358 |      /** | |
| 359 |       * <p> Constructs a <code>MultipartStream</code> with a default size buffer. | |
| 360 |       * | |
| 361 |       * @param input    The <code>InputStream</code> to serve as a data source. | |
| 362 |       * @param boundary The token used for dividing the stream into | |
| 363 |       *                 <code>encapsulations</code>. | |
| 364 |       * @param pNotifier An object for calling the progress listener, if any. | |
| 365 |       * | |
| 366 |       * | |
| 367 |       * @see #MultipartStream(InputStream, byte[], int, ProgressNotifier) | |
| 368 |       */ | |
| 369 | MultipartStream(InputStream input, | |
| 370 |              byte[] boundary, | |
| 371 |              ProgressNotifier pNotifier) { | |
| 372 | 37 |          this(input, boundary, DEFAULT_BUFSIZE, pNotifier); | 
| 373 | 37 | } | 
| 374 | ||
| 375 |      /** | |
| 376 |       * <p> Constructs a <code>MultipartStream</code> with a default size buffer. | |
| 377 |       * | |
| 378 |       * @param input    The <code>InputStream</code> to serve as a data source. | |
| 379 |       * @param boundary The token used for dividing the stream into | |
| 380 |       *                 <code>encapsulations</code>. | |
| 381 |       * | |
| 382 |       * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int, | |
| 383 |       *  ProgressNotifier)}. | |
| 384 |       */ | |
| 385 | @Deprecated | |
| 386 |      public MultipartStream(InputStream input, | |
| 387 |              byte[] boundary) { | |
| 388 | 0 |          this(input, boundary, DEFAULT_BUFSIZE, null); | 
| 389 | 0 |      } | 
| 390 | ||
| 391 |      // --------------------------------------------------------- Public methods | |
| 392 | ||
| 393 |      /** | |
| 394 |       * Retrieves the character encoding used when reading the headers of an | |
| 395 |       * individual part. When not specified, or <code>null</code>, the platform | |
| 396 |       * default encoding is used. | |
| 397 |       * | |
| 398 |       * @return The encoding used to read part headers. | |
| 399 |       */ | |
| 400 |      public String getHeaderEncoding() { | |
| 401 | 0 |          return headerEncoding; | 
| 402 | } | |
| 403 | ||
| 404 |      /** | |
| 405 |       * Specifies the character encoding to be used when reading the headers of | |
| 406 |       * individual parts. When not specified, or <code>null</code>, the platform | |
| 407 |       * default encoding is used. | |
| 408 |       * | |
| 409 |       * @param encoding The encoding used to read part headers. | |
| 410 |       */ | |
| 411 | public void setHeaderEncoding(String encoding) { | |
| 412 | 36 | headerEncoding = encoding; | 
| 413 | 36 | } | 
| 414 | ||
| 415 |      /** | |
| 416 |       * Reads a byte from the <code>buffer</code>, and refills it as | |
| 417 |       * necessary. | |
| 418 |       * | |
| 419 |       * @return The next byte from the input stream. | |
| 420 |       * | |
| 421 |       * @throws IOException if there is no more data available. | |
| 422 |       */ | |
| 423 | public byte readByte() throws IOException { | |
| 424 |          // Buffer depleted ? | |
| 425 | 170498 | if (head == tail) { | 
| 426 | 71 | head = 0; | 
| 427 |              // Refill. | |
| 428 | 71 | tail = input.read(buffer, head, bufSize); | 
| 429 | 71 |              if (tail == -1) { | 
| 430 |                  // No more data available. | |
| 431 | 0 |                  throw new IOException("No more data is available"); | 
| 432 | } | |
| 433 | 71 |              if (notifier != null) { | 
| 434 | 71 | notifier.noteBytesRead(tail); | 
| 435 | } | |
| 436 | } | |
| 437 | 170498 |          return buffer[head++]; | 
| 438 | } | |
| 439 | ||
| 440 |      /** | |
| 441 |       * Skips a <code>boundary</code> token, and checks whether more | |
| 442 |       * <code>encapsulations</code> are contained in the stream. | |
| 443 |       * | |
| 444 |       * @return <code>true</code> if there are more encapsulations in | |
| 445 |       *         this stream; <code>false</code> otherwise. | |
| 446 |       * | |
| 447 |       * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits | |
| 448 |       * @throws MalformedStreamException if the stream ends unexpectedly or | |
| 449 |       *                                  fails to follow required syntax. | |
| 450 |       */ | |
| 451 | public boolean readBoundary() | |
| 452 |              throws FileUploadIOException, MalformedStreamException { | |
| 453 | 3228 | byte[] marker = new byte[2]; | 
| 454 | 3228 | boolean nextChunk = false; | 
| 455 | ||
| 456 | 3228 | head += boundaryLength; | 
| 457 |          try { | |
| 458 | 3228 | marker[0] = readByte(); | 
| 459 | 3228 | if (marker[0] == LF) { | 
| 460 |                  // Work around IE5 Mac bug with input type=image. | |
| 461 |                  // Because the boundary delimiter, not including the trailing | |
| 462 |                  // CRLF, must not appear within any file (RFC 2046, section | |
| 463 |                  // 5.1.1), we know the missing CR is due to a buggy browser | |
| 464 |                  // rather than a file containing something similar to a | |
| 465 |                  // boundary. | |
| 466 | 4 | return true; | 
| 467 | } | |
| 468 | ||
| 469 | 3224 | marker[1] = readByte(); | 
| 470 | 3224 | if (arrayequals(marker, STREAM_TERMINATOR, 2)) { | 
| 471 | 33 |                  nextChunk = false; | 
| 472 | 3191 |              } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) { | 
| 473 | 3191 |                  nextChunk = true; | 
| 474 |              } else { | |
| 475 | 0 |                  throw new MalformedStreamException( | 
| 476 |                  "Unexpected characters follow a boundary"); | |
| 477 | } | |
| 478 | 0 |          } catch (FileUploadIOException e) { | 
| 479 |              // wraps a SizeException, re-throw as it will be unwrapped later | |
| 480 | 0 |              throw e; | 
| 481 | 0 |          } catch (IOException e) { | 
| 482 | 0 |              throw new MalformedStreamException("Stream ended unexpectedly"); | 
| 483 | 3224 | } | 
| 484 | 3224 |          return nextChunk; | 
| 485 | } | |
| 486 | ||
| 487 |      /** | |
| 488 |       * <p>Changes the boundary token used for partitioning the stream. | |
| 489 |       * | |
| 490 |       * <p>This method allows single pass processing of nested multipart | |
| 491 |       * streams. | |
| 492 |       * | |
| 493 |       * <p>The boundary token of the nested stream is <code>required</code> | |
| 494 |       * to be of the same length as the boundary token in parent stream. | |
| 495 |       * | |
| 496 |       * <p>Restoring the parent stream boundary token after processing of a | |
| 497 |       * nested stream is left to the application. | |
| 498 |       * | |
| 499 |       * @param boundary The boundary to be used for parsing of the nested | |
| 500 |       *                 stream. | |
| 501 |       * | |
| 502 |       * @throws IllegalBoundaryException if the <code>boundary</code> | |
| 503 |       *                                  has a different length than the one | |
| 504 |       *                                  being currently parsed. | |
| 505 |       */ | |
| 506 | public void setBoundary(byte[] boundary) | |
| 507 |              throws IllegalBoundaryException { | |
| 508 | 8 |          if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) { | 
| 509 | 0 |              throw new IllegalBoundaryException( | 
| 510 |              "The length of a boundary token cannot be changed"); | |
| 511 | } | |
| 512 | 8 |          System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, | 
| 513 | boundary.length); | |
| 514 | 8 | computeBoundaryTable(); | 
| 515 | 8 | } | 
| 516 | ||
| 517 |      /** | |
| 518 |       * Compute the table used for Knuth-Morris-Pratt search algorithm. | |
| 519 |       */ | |
| 520 | private void computeBoundaryTable() { | |
| 521 | 6504 |          int position = 2; | 
| 522 | 6504 |          int candidate = 0; | 
| 523 | ||
| 524 | 6504 | boundaryTable[0] = -1; | 
| 525 | 6504 | boundaryTable[1] = 0; | 
| 526 | ||
| 527 | 77942 | while (position <= boundaryLength) { | 
| 528 | 71438 | if (boundary[position - 1] == boundary[candidate]) { | 
| 529 | 12880 | boundaryTable[position] = candidate + 1; | 
| 530 | 12880 | candidate++; | 
| 531 | 12880 | position++; | 
| 532 | 58558 | } else if (candidate > 0) { | 
| 533 | 12880 | candidate = boundaryTable[candidate]; | 
| 534 |              } else { | |
| 535 | 45678 | boundaryTable[position] = 0; | 
| 536 | 45678 | position++; | 
| 537 | } | |
| 538 | } | |
| 539 | 6504 | } | 
| 540 | ||
| 541 |      /** | |
| 542 |       * <p>Reads the <code>header-part</code> of the current | |
| 543 |       * <code>encapsulation</code>. | |
| 544 |       * | |
| 545 |       * <p>Headers are returned verbatim to the input stream, including the | |
| 546 |       * trailing <code>CRLF</code> marker. Parsing is left to the | |
| 547 |       * application. | |
| 548 |       * | |
| 549 |       * <p><strong>TODO</strong> allow limiting maximum header size to | |
| 550 |       * protect against abuse. | |
| 551 |       * | |
| 552 |       * @return The <code>header-part</code> of the current encapsulation. | |
| 553 |       * | |
| 554 |       * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits. | |
| 555 |       * @throws MalformedStreamException if the stream ends unexpectedly. | |
| 556 |       */ | |
| 557 | public String readHeaders() throws FileUploadIOException, MalformedStreamException { | |
| 558 | 3195 |          int i = 0; | 
| 559 |          byte b; | |
| 560 |          // to support multi-byte characters | |
| 561 | 3195 |          ByteArrayOutputStream baos = new ByteArrayOutputStream(); | 
| 562 | 3195 |          int size = 0; | 
| 563 | 167241 | while (i < HEADER_SEPARATOR.length) { | 
| 564 |              try { | |
| 565 | 164046 | b = readByte(); | 
| 566 | 0 |              } catch (FileUploadIOException e) { | 
| 567 |                  // wraps a SizeException, re-throw as it will be unwrapped later | |
| 568 | 0 |                  throw e; | 
| 569 | 0 |              } catch (IOException e) { | 
| 570 | 0 |                  throw new MalformedStreamException("Stream ended unexpectedly"); | 
| 571 | 164046 | } | 
| 572 | 164046 |              if (++size > HEADER_PART_SIZE_MAX) { | 
| 573 | 0 |                  throw new MalformedStreamException( | 
| 574 | 0 |                          format("Header section has more than %s bytes (maybe it is not properly terminated)", | 
| 575 | 0 |                                 Integer.valueOf(HEADER_PART_SIZE_MAX))); | 
| 576 | } | |
| 577 | 164046 | if (b == HEADER_SEPARATOR[i]) { | 
| 578 | 12888 | i++; | 
| 579 |              } else { | |
| 580 | 151158 | i = 0; | 
| 581 | } | |
| 582 | 164046 | baos.write(b); | 
| 583 | } | |
| 584 | ||
| 585 | 3195 |          String headers = null; | 
| 586 | 3195 |          if (headerEncoding != null) { | 
| 587 |              try { | |
| 588 | 0 |                  headers = baos.toString(headerEncoding); | 
| 589 | 0 |              } catch (UnsupportedEncodingException e) { | 
| 590 |                  // Fall back to platform default if specified encoding is not | |
| 591 |                  // supported. | |
| 592 | 0 |                  headers = baos.toString(); | 
| 593 | 0 |              } | 
| 594 |          } else { | |
| 595 | 3195 | headers = baos.toString(); | 
| 596 | } | |
| 597 | ||
| 598 | 3195 |          return headers; | 
| 599 | } | |
| 600 | ||
| 601 |      /** | |
| 602 |       * <p>Reads <code>body-data</code> from the current | |
| 603 |       * <code>encapsulation</code> and writes its contents into the | |
| 604 |       * output <code>Stream</code>. | |
| 605 |       * | |
| 606 |       * <p>Arbitrary large amounts of data can be processed by this | |
| 607 |       * method using a constant size buffer. (see {@link | |
| 608 |       * #MultipartStream(InputStream,byte[],int, | |
| 609 |       *   MultipartStream.ProgressNotifier) constructor}). | |
| 610 |       * | |
| 611 |       * @param output The <code>Stream</code> to write data into. May | |
| 612 |       *               be null, in which case this method is equivalent | |
| 613 |       *               to {@link #discardBodyData()}. | |
| 614 |       * | |
| 615 |       * @return the amount of data written. | |
| 616 |       * | |
| 617 |       * @throws MalformedStreamException if the stream ends unexpectedly. | |
| 618 |       * @throws IOException              if an i/o error occurs. | |
| 619 |       */ | |
| 620 | public int readBodyData(OutputStream output) | |
| 621 |              throws MalformedStreamException, IOException { | |
| 622 | 3229 | return (int) Streams.copy(newInputStream(), output, false); // N.B. Streams.copy closes the input stream | 
| 623 | } | |
| 624 | ||
| 625 |      /** | |
| 626 |       * Creates a new {@link ItemInputStream}. | |
| 627 |       * @return A new instance of {@link ItemInputStream}. | |
| 628 |       */ | |
| 629 |      ItemInputStream newInputStream() { | |
| 630 | 6419 | return new ItemInputStream(); | 
| 631 | } | |
| 632 | ||
| 633 |      /** | |
| 634 |       * <p> Reads <code>body-data</code> from the current | |
| 635 |       * <code>encapsulation</code> and discards it. | |
| 636 |       * | |
| 637 |       * <p>Use this method to skip encapsulations you don't need or don't | |
| 638 |       * understand. | |
| 639 |       * | |
| 640 |       * @return The amount of data discarded. | |
| 641 |       * | |
| 642 |       * @throws MalformedStreamException if the stream ends unexpectedly. | |
| 643 |       * @throws IOException              if an i/o error occurs. | |
| 644 |       */ | |
| 645 | public int discardBodyData() throws MalformedStreamException, IOException { | |
| 646 | 3229 | return readBodyData(null); | 
| 647 | } | |
| 648 | ||
| 649 |      /** | |
| 650 |       * Finds the beginning of the first <code>encapsulation</code>. | |
| 651 |       * | |
| 652 |       * @return <code>true</code> if an <code>encapsulation</code> was found in | |
| 653 |       *         the stream. | |
| 654 |       * | |
| 655 |       * @throws IOException if an i/o error occurs. | |
| 656 |       */ | |
| 657 | public boolean skipPreamble() throws IOException { | |
| 658 |          // First delimiter may be not preceeded with a CRLF. | |
| 659 | 3229 | System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2); | 
| 660 | 3229 | boundaryLength = boundary.length - 2; | 
| 661 | 3229 | computeBoundaryTable(); | 
| 662 |          try { | |
| 663 |              // Discard all data up to the delimiter. | |
| 664 | 3229 | discardBodyData(); | 
| 665 | ||
| 666 |              // Read boundary - if succeeded, the stream contains an | |
| 667 |              // encapsulation. | |
| 668 | 6456 |              return readBoundary(); | 
| 669 | 0 |          } catch (MalformedStreamException e) { | 
| 670 | 0 |              return false; | 
| 671 |          } finally { | |
| 672 |              // Restore delimiter. | |
| 673 | 3229 | System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2); | 
| 674 | 3229 | boundaryLength = boundary.length; | 
| 675 | 3229 | boundary[0] = CR; | 
| 676 | 3229 | boundary[1] = LF; | 
| 677 | 3229 | computeBoundaryTable(); | 
| 678 | 1 | } | 
| 679 | } | |
| 680 | ||
| 681 |      /** | |
| 682 |       * Compares <code>count</code> first bytes in the arrays | |
| 683 |       * <code>a</code> and <code>b</code>. | |
| 684 |       * | |
| 685 |       * @param a     The first array to compare. | |
| 686 |       * @param b     The second array to compare. | |
| 687 |       * @param count How many bytes should be compared. | |
| 688 |       * | |
| 689 |       * @return <code>true</code> if <code>count</code> first bytes in arrays | |
| 690 |       *         <code>a</code> and <code>b</code> are equal. | |
| 691 |       */ | |
| 692 | public static boolean arrayequals(byte[] a, | |
| 693 |              byte[] b, | |
| 694 |              int count) { | |
| 695 | 12863 | for (int i = 0; i < count; i++) { | 
| 696 | 9639 | if (a[i] != b[i]) { | 
| 697 | 3191 | return false; | 
| 698 | } | |
| 699 | } | |
| 700 | 3224 | return true; | 
| 701 | } | |
| 702 | ||
| 703 |      /** | |
| 704 |       * Searches for a byte of specified value in the <code>buffer</code>, | |
| 705 |       * starting at the specified <code>position</code>. | |
| 706 |       * | |
| 707 |       * @param value The value to find. | |
| 708 |       * @param pos   The starting position for searching. | |
| 709 |       * | |
| 710 |       * @return The position of byte found, counting from beginning of the | |
| 711 |       *         <code>buffer</code>, or <code>-1</code> if not found. | |
| 712 |       */ | |
| 713 | protected int findByte(byte value, | |
| 714 |              int pos) { | |
| 715 | 0 |          for (int i = pos; i < tail; i++) { | 
| 716 | 0 |              if (buffer[i] == value) { | 
| 717 | 0 |                  return i; | 
| 718 | } | |
| 719 | } | |
| 720 | ||
| 721 | 0 |          return -1; | 
| 722 | } | |
| 723 | ||
| 724 |      /** | |
| 725 |       * Searches for the <code>boundary</code> in the <code>buffer</code> | |
| 726 |       * region delimited by <code>head</code> and <code>tail</code>. | |
| 727 |       * | |
| 728 |       * @return The position of the boundary found, counting from the | |
| 729 |       *         beginning of the <code>buffer</code>, or <code>-1</code> if | |
| 730 |       *         not found. | |
| 731 |       */ | |
| 732 | protected int findSeparator() { | |
| 733 | ||
| 734 | 14855 | int bufferPos = this.head; | 
| 735 | 14855 |          int tablePos = 0; | 
| 736 | ||
| 737 | 34380763 | while (bufferPos < this.tail) { | 
| 738 | 68680474 | while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos]) { | 
| 739 | 34308150 | tablePos = boundaryTable[tablePos]; | 
| 740 | } | |
| 741 | 34372324 | bufferPos++; | 
| 742 | 34372324 | tablePos++; | 
| 743 | 34372324 | if (tablePos == boundaryLength) { | 
| 744 | 6416 |                  return bufferPos - boundaryLength; | 
| 745 | } | |
| 746 | } | |
| 747 | 8439 |          return -1; | 
| 748 | } | |
| 749 | ||
| 750 |      /** | |
| 751 |       * Thrown to indicate that the input stream fails to follow the | |
| 752 |       * required syntax. | |
| 753 |       */ | |
| 754 | public static class MalformedStreamException extends IOException { | |
| 755 | ||
| 756 |          /** | |
| 757 |           * The UID to use when serializing this instance. | |
| 758 |           */ | |
| 759 | private static final long serialVersionUID = 6466926458059796677L; | |
| 760 | ||
| 761 |          /** | |
| 762 |           * Constructs a <code>MalformedStreamException</code> with no | |
| 763 |           * detail message. | |
| 764 |           */ | |
| 765 |          public MalformedStreamException() { | |
| 766 | 0 |              super(); | 
| 767 | 0 |          } | 
| 768 | ||
| 769 |          /** | |
| 770 |           * Constructs an <code>MalformedStreamException</code> with | |
| 771 |           * the specified detail message. | |
| 772 |           * | |
| 773 |           * @param message The detail message. | |
| 774 |           */ | |
| 775 |          public MalformedStreamException(String message) { | |
| 776 | 2 |              super(message); | 
| 777 | 2 | } | 
| 778 | ||
| 779 | } | |
| 780 | ||
| 781 |      /** | |
| 782 |       * Thrown upon attempt of setting an invalid boundary token. | |
| 783 |       */ | |
| 784 | public static class IllegalBoundaryException extends IOException { | |
| 785 | ||
| 786 |          /** | |
| 787 |           * The UID to use when serializing this instance. | |
| 788 |           */ | |
| 789 | private static final long serialVersionUID = -161533165102632918L; | |
| 790 | ||
| 791 |          /** | |
| 792 |           * Constructs an <code>IllegalBoundaryException</code> with no | |
| 793 |           * detail message. | |
| 794 |           */ | |
| 795 |          public IllegalBoundaryException() { | |
| 796 | 0 |              super(); | 
| 797 | 0 |          } | 
| 798 | ||
| 799 |          /** | |
| 800 |           * Constructs an <code>IllegalBoundaryException</code> with | |
| 801 |           * the specified detail message. | |
| 802 |           * | |
| 803 |           * @param message The detail message. | |
| 804 |           */ | |
| 805 |          public IllegalBoundaryException(String message) { | |
| 806 | 0 |              super(message); | 
| 807 | 0 |          } | 
| 808 | ||
| 809 | } | |
| 810 | ||
| 811 |      /** | |
| 812 |       * An {@link InputStream} for reading an items contents. | |
| 813 |       */ | |
| 814 | public class ItemInputStream extends InputStream implements Closeable { | |
| 815 | ||
| 816 |          /** | |
| 817 |           * The number of bytes, which have been read so far. | |
| 818 |           */ | |
| 819 | private long total; | |
| 820 | ||
| 821 |          /** | |
| 822 |           * The number of bytes, which must be hold, because | |
| 823 |           * they might be a part of the boundary. | |
| 824 |           */ | |
| 825 | private int pad; | |
| 826 | ||
| 827 |          /** | |
| 828 |           * The current offset in the buffer. | |
| 829 |           */ | |
| 830 | private int pos; | |
| 831 | ||
| 832 |          /** | |
| 833 |           * Whether the stream is already closed. | |
| 834 |           */ | |
| 835 | private boolean closed; | |
| 836 | ||
| 837 |          /** | |
| 838 |           * Creates a new instance. | |
| 839 |           */ | |
| 840 | 6419 |          ItemInputStream() { | 
| 841 | 6419 | findSeparator(); | 
| 842 | 6419 | } | 
| 843 | ||
| 844 |          /** | |
| 845 |           * Called for finding the separator. | |
| 846 |           */ | |
| 847 | private void findSeparator() { | |
| 848 | 14855 |              pos = MultipartStream.this.findSeparator(); | 
| 849 | 14855 | if (pos == -1) { | 
| 850 | 8439 | if (tail - head > keepRegion) { | 
| 851 | 8374 | pad = keepRegion; | 
| 852 |                  } else { | |
| 853 | 65 | pad = tail - head; | 
| 854 | } | |
| 855 | } | |
| 856 | 14855 | } | 
| 857 | ||
| 858 |          /** | |
| 859 |           * Returns the number of bytes, which have been read | |
| 860 |           * by the stream. | |
| 861 |           * | |
| 862 |           * @return Number of bytes, which have been read so far. | |
| 863 |           */ | |
| 864 | public long getBytesRead() { | |
| 865 | 0 |              return total; | 
| 866 | } | |
| 867 | ||
| 868 |          /** | |
| 869 |           * Returns the number of bytes, which are currently | |
| 870 |           * available, without blocking. | |
| 871 |           * | |
| 872 |           * @throws IOException An I/O error occurs. | |
| 873 |           * @return Number of bytes in the buffer. | |
| 874 |           */ | |
| 875 | @Override | |
| 876 | public int available() throws IOException { | |
| 877 | 17069673 | if (pos == -1) { | 
| 878 | 14948730 |                  return tail - head - pad; | 
| 879 | } | |
| 880 | 2120943 |              return pos - head; | 
| 881 | } | |
| 882 | ||
| 883 |          /** | |
| 884 |           * Offset when converting negative bytes to integers. | |
| 885 |           */ | |
| 886 | private static final int BYTE_POSITIVE_OFFSET = 256; | |
| 887 | ||
| 888 |          /** | |
| 889 |           * Returns the next byte in the stream. | |
| 890 |           * | |
| 891 |           * @return The next byte in the stream, as a non-negative | |
| 892 |           *   integer, or -1 for EOF. | |
| 893 |           * @throws IOException An I/O error occurred. | |
| 894 |           */ | |
| 895 | @Override | |
| 896 | public int read() throws IOException { | |
| 897 | 17039872 |              if (closed) { | 
| 898 | 0 |                  throw new FileItemStream.ItemSkippedException(); | 
| 899 | } | |
| 900 | 17039872 | if (available() == 0 && makeAvailable() == 0) { | 
| 901 | 1024 |                  return -1; | 
| 902 | } | |
| 903 | 17038848 | ++total; | 
| 904 | 17038848 |              int b = buffer[head++]; | 
| 905 | 17038848 | if (b >= 0) { | 
| 906 | 8552192 |                  return b; | 
| 907 | } | |
| 908 | 8486656 |              return b + BYTE_POSITIVE_OFFSET; | 
| 909 | } | |
| 910 | ||
| 911 |          /** | |
| 912 |           * Reads bytes into the given buffer. | |
| 913 |           * | |
| 914 |           * @param b The destination buffer, where to write to. | |
| 915 |           * @param off Offset of the first byte in the buffer. | |
| 916 |           * @param len Maximum number of bytes to read. | |
| 917 |           * @return Number of bytes, which have been actually read, | |
| 918 |           *   or -1 for EOF. | |
| 919 |           * @throws IOException An I/O error occurred. | |
| 920 |           */ | |
| 921 | @Override | |
| 922 | public int read(byte[] b, int off, int len) throws IOException { | |
| 923 | 14949 |              if (closed) { | 
| 924 | 0 |                  throw new FileItemStream.ItemSkippedException(); | 
| 925 | } | |
| 926 | 14949 |              if (len == 0) { | 
| 927 | 0 |                  return 0; | 
| 928 | } | |
| 929 | 14949 |              int res = available(); | 
| 930 | 14949 | if (res == 0) { | 
| 931 | 9615 | res = makeAvailable(); | 
| 932 | 9612 | if (res == 0) { | 
| 933 | 5389 |                      return -1; | 
| 934 | } | |
| 935 | } | |
| 936 | 9557 | res = Math.min(res, len); | 
| 937 | 9557 | System.arraycopy(buffer, head, b, off, res); | 
| 938 | 9557 | head += res; | 
| 939 | 9557 | total += res; | 
| 940 | 9557 |              return res; | 
| 941 | } | |
| 942 | ||
| 943 |          /** | |
| 944 |           * Closes the input stream. | |
| 945 |           * | |
| 946 |           * @throws IOException An I/O error occurred. | |
| 947 |           */ | |
| 948 | @Override | |
| 949 | public void close() throws IOException { | |
| 950 | 9603 |              close(false); | 
| 951 | 9600 | } | 
| 952 | ||
| 953 |          /** | |
| 954 |           * Closes the input stream. | |
| 955 |           * | |
| 956 |           * @param pCloseUnderlying Whether to close the underlying stream | |
| 957 |           *   (hard close) | |
| 958 |           * @throws IOException An I/O error occurred. | |
| 959 |           */ | |
| 960 | public void close(boolean pCloseUnderlying) throws IOException { | |
| 961 | 9605 | if (closed) { | 
| 962 | 3187 |                  return; | 
| 963 | } | |
| 964 | 6418 | if (pCloseUnderlying) { | 
| 965 | 2 |                  closed = true; | 
| 966 | 2 | input.close(); | 
| 967 |              } else { | |
| 968 |                  for (;;) { | |
| 969 | 6416 |                      int av = available(); | 
| 970 | 6416 |                      if (av == 0) { | 
| 971 | 6416 | av = makeAvailable(); | 
| 972 | 6413 |                          if (av == 0) { | 
| 973 | 6413 |                              break; | 
| 974 | } | |
| 975 | } | |
| 976 | 0 |                      skip(av); | 
| 977 | 0 |                  } | 
| 978 | } | |
| 979 | 6415 |              closed = true; | 
| 980 | 6415 | } | 
| 981 | ||
| 982 |          /** | |
| 983 |           * Skips the given number of bytes. | |
| 984 |           * | |
| 985 |           * @param bytes Number of bytes to skip. | |
| 986 |           * @return The number of bytes, which have actually been | |
| 987 |           *   skipped. | |
| 988 |           * @throws IOException An I/O error occurred. | |
| 989 |           */ | |
| 990 | @Override | |
| 991 | public long skip(long bytes) throws IOException { | |
| 992 | 0 |              if (closed) { | 
| 993 | 0 |                  throw new FileItemStream.ItemSkippedException(); | 
| 994 | } | |
| 995 | 0 |              int av = available(); | 
| 996 | 0 |              if (av == 0) { | 
| 997 | 0 |                  av = makeAvailable(); | 
| 998 | 0 |                  if (av == 0) { | 
| 999 | 0 |                      return 0; | 
| 1000 | } | |
| 1001 | } | |
| 1002 | 0 |              long res = Math.min(av, bytes); | 
| 1003 | 0 |              head += res; | 
| 1004 | 0 |              return res; | 
| 1005 | } | |
| 1006 | ||
| 1007 |          /** | |
| 1008 |           * Attempts to read more data. | |
| 1009 |           * | |
| 1010 |           * @return Number of available bytes | |
| 1011 |           * @throws IOException An I/O error occurred. | |
| 1012 |           */ | |
| 1013 | private int makeAvailable() throws IOException { | |
| 1014 | 21227 | if (pos != -1) { | 
| 1015 | 12791 |                  return 0; | 
| 1016 | } | |
| 1017 | ||
| 1018 |              // Move the data to the beginning of the buffer. | |
| 1019 | 8436 | total += tail - head - pad; | 
| 1020 | 8436 | System.arraycopy(buffer, tail - pad, buffer, 0, pad); | 
| 1021 | ||
| 1022 |              // Refill buffer with new data. | |
| 1023 | 8436 | head = 0; | 
| 1024 | 8436 | tail = pad; | 
| 1025 | ||
| 1026 |              for (;;) { | |
| 1027 | 8442 |                  int bytesRead = input.read(buffer, tail, bufSize - tail); | 
| 1028 | 8438 | if (bytesRead == -1) { | 
| 1029 |                      // The last pad amount is left in the buffer. | |
| 1030 |                      // Boundary can't be in there so signal an error | |
| 1031 |                      // condition. | |
| 1032 | 2 | final String msg = "Stream ended unexpectedly"; | 
| 1033 | 2 | throw new MalformedStreamException(msg); | 
| 1034 | } | |
| 1035 | 8436 |                  if (notifier != null) { | 
| 1036 | 8436 | notifier.noteBytesRead(bytesRead); | 
| 1037 | } | |
| 1038 | 8436 | tail += bytesRead; | 
| 1039 | ||
| 1040 | 8436 | findSeparator(); | 
| 1041 | 8436 |                  int av = available(); | 
| 1042 | ||
| 1043 | 8436 | if (av > 0 || pos != -1) { | 
| 1044 | 8430 |                      return av; | 
| 1045 | } | |
| 1046 | 6 | } | 
| 1047 | } | |
| 1048 | ||
| 1049 |          /** | |
| 1050 |           * Returns, whether the stream is closed. | |
| 1051 |           * | |
| 1052 |           * @return True, if the stream is closed, otherwise false. | |
| 1053 |           */ | |
| 1054 | @Override | |
| 1055 | public boolean isClosed() { | |
| 1056 | 3185 |              return closed; | 
| 1057 | } | |
| 1058 | ||
| 1059 | } | |
| 1060 | ||
| 1061 | } |