1 /****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one *
3 * or more contributor license agreements. See the NOTICE file *
4 * distributed with this work for additional information *
5 * regarding copyright ownership. The ASF licenses this file *
6 * to you under the Apache License, Version 2.0 (the *
7 * "License"); you may not use this file except in compliance *
8 * with the License. You may obtain a copy of the License at *
10 * http://www.apache.org/licenses/LICENSE-2.0 *
12 * Unless required by applicable law or agreed to in writing, *
13 * software distributed under the License is distributed on an *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15 * KIND, either express or implied. See the License for the *
16 * specific language governing permissions and limitations *
17 * under the License. *
18 ****************************************************************/
20 package org
.apache
.james
.mime4j
;
22 import java
.io
.IOException
;
25 * Stream that constrains itself to a single MIME body part.
26 * After the stream ends (i.e. read() returns -1) {@link #isLastPart()}
27 * can be used to determine if a final boundary has been seen or not.
29 * @version $Id: MimeBoundaryInputStream.java,v 1.2 2004/11/29 13:15:42 ntherning Exp $
31 public class MimeBoundaryInputStream
extends BufferingInputStream
{
33 private final InputBuffer buffer
;
34 private final byte[] boundary
;
38 private boolean atBoundary
;
39 private int boundaryLen
;
40 private boolean lastPart
;
41 private boolean completed
;
44 * Creates a new MimeBoundaryInputStream.
45 * @param s The underlying stream.
46 * @param boundary Boundary string (not including leading hyphens).
48 public MimeBoundaryInputStream(InputBuffer inbuffer
, String boundary
)
50 this.buffer
= inbuffer
;
53 this.atBoundary
= false;
55 this.lastPart
= false;
56 this.completed
= false;
58 this.boundary
= new byte[boundary
.length() + 2];
59 this.boundary
[0] = (byte) '-';
60 this.boundary
[1] = (byte) '-';
61 for (int i
= 0; i
< boundary
.length(); i
++) {
62 byte ch
= (byte) boundary
.charAt(i
);
63 if (ch
== '\r' || ch
== '\n') {
64 throw new IllegalArgumentException("Boundary may not contain CR or LF");
66 this.boundary
[i
+ 2] = ch
;
72 * Closes the underlying stream.
74 * @throws IOException on I/O errors.
76 public void close() throws IOException
{
80 * @see java.io.InputStream#markSupported()
82 public boolean markSupported() {
87 * @see java.io.InputStream#read()
89 public int read() throws IOException
{
93 if (endOfStream() && !hasData()) {
100 } else if (endOfStream()) {
108 public int read(byte[] b
, int off
, int len
) throws IOException
{
112 if (endOfStream() && !hasData()) {
120 int chunk
= Math
.min(len
, limit
- buffer
.pos());
121 return buffer
.read(b
, off
, chunk
);
124 public int readLine(final ByteArrayBuffer dst
) throws IOException
{
126 throw new IllegalArgumentException("Destination buffer may not be null");
131 if (endOfStream() && !hasData()) {
137 boolean found
= false;
141 bytesRead
= fillBuffer();
142 if (!hasData() && endOfStream()) {
148 int len
= this.limit
- this.buffer
.pos();
149 int i
= this.buffer
.indexOf((byte)'\n', this.buffer
.pos(), len
);
153 chunk
= i
+ 1 - this.buffer
.pos();
158 dst
.append(this.buffer
.buf(), this.buffer
.pos(), chunk
);
159 this.buffer
.skip(chunk
);
163 if (total
== 0 && bytesRead
== -1) {
170 private boolean endOfStream() {
171 return eof
|| atBoundary
;
174 private boolean hasData() {
175 return limit
> buffer
.pos() && limit
< buffer
.limit();
178 private int fillBuffer() throws IOException
{
184 bytesRead
= buffer
.fillBuffer();
188 eof
= bytesRead
== -1;
190 int i
= buffer
.indexOf(boundary
);
194 calculateBoundaryLen();
197 limit
= buffer
.limit();
199 limit
= buffer
.limit() - (boundary
.length
+ 1);
200 // \r\n + (boundary - one char)
206 private void calculateBoundaryLen() throws IOException
{
207 boundaryLen
= boundary
.length
;
208 int len
= limit
- buffer
.pos();
210 if (buffer
.charAt(limit
- 1) == '\n') {
216 if (buffer
.charAt(limit
- 1) == '\r') {
223 private void skipBoundary() throws IOException
{
226 buffer
.skip(boundaryLen
);
228 if (buffer
.length() > 1) {
229 int ch1
= buffer
.charAt(buffer
.pos());
230 int ch2
= buffer
.charAt(buffer
.pos() + 1);
231 if (ch1
== '-' && ch2
== '-') {
232 this.lastPart
= true;
247 private void skipLineDelimiter() {
250 int len
= buffer
.length();
252 ch1
= buffer
.charAt(buffer
.pos());
255 ch2
= buffer
.charAt(buffer
.pos() + 1);
257 if (ch1
== '\r' && ch2
== '\n') {
259 } else if (ch1
== '\n') {
264 public boolean isLastPart() {
268 public boolean eof() {
269 return eof
&& !buffer
.hasBufferedData();
272 public String
toString() {
273 final StringBuffer buffer
= new StringBuffer("MimeBoundaryInputStream, boundary ");
274 for (int i
= 0; i
< boundary
.length
; i
++) {
275 buffer
.append((char) boundary
[i
]);
277 return buffer
.toString();