source: contrib/MailArchiver/sources/vendor/mime4j/custom/core/src/main/java/org/apache/james/mime4j/io/MimeBoundaryInputStream.java @ 6785

Revision 6785, 9.6 KB checked in by rafaelraymundo, 12 years ago (diff)

Ticket #2946 - Liberado codigo do MailArchiver?. Documentação na subpasta DOCS.

Line 
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   *
9 *                                                              *
10 *   http://www.apache.org/licenses/LICENSE-2.0                 *
11 *                                                              *
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 ****************************************************************/
19
20package org.apache.james.mime4j.io;
21
22import org.apache.james.mime4j.util.ByteArrayBuffer;
23
24import java.io.IOException;
25
26/**
27 * Stream that constrains itself to a single MIME body part.
28 * After the stream ends (i.e. read() returns -1) {@link #isLastPart()}
29 * can be used to determine if a final boundary has been seen or not.
30 */
31public class MimeBoundaryInputStream extends LineReaderInputStream {
32
33    private final byte[] boundary;
34   
35    private boolean eof;
36    private int limit;
37    private boolean atBoundary;
38    private int boundaryLen;
39    private boolean lastPart;
40    private boolean completed;
41
42    private BufferedLineReaderInputStream buffer;
43
44    /**
45     * Store the first buffer length.
46     * Used to distinguish between an empty preamble and
47     * no preamble.
48     */
49    private int initialLength;
50
51    /**
52     * Creates a new MimeBoundaryInputStream.
53     *
54     * @param inbuffer The underlying stream.
55     * @param boundary Boundary string (not including leading hyphens).
56     * @throws IllegalArgumentException when boundary is too long
57     */
58    public MimeBoundaryInputStream(BufferedLineReaderInputStream inbuffer, String boundary)
59            throws IOException {
60        super(inbuffer);
61        int bufferSize = 2 * boundary.length();
62        if (bufferSize < 4096) {
63            bufferSize = 4096;
64        }
65        inbuffer.ensureCapacity(bufferSize);
66        this.buffer = inbuffer;
67        this.eof = false;
68        this.limit = -1;
69        this.atBoundary = false;
70        this.boundaryLen = 0;
71        this.lastPart = false;
72        this.initialLength = -1;
73        this.completed = false;
74       
75        this.boundary = new byte[boundary.length() + 2];
76        this.boundary[0] = (byte) '-';
77        this.boundary[1] = (byte) '-';
78        for (int i = 0; i < boundary.length(); i++) {
79            byte ch = (byte) boundary.charAt(i);
80            this.boundary[i + 2] = ch;
81        }
82       
83        fillBuffer();
84    }
85
86    /**
87     * Closes the underlying stream.
88     *
89     * @throws IOException on I/O errors.
90     */
91    @Override
92    public void close() throws IOException {
93    }
94
95    /**
96     * @see java.io.InputStream#markSupported()
97     */
98    @Override
99    public boolean markSupported() {
100        return false;
101    }
102   
103    public boolean readAllowed() throws IOException {
104        if (completed) {
105            return false;
106        }
107        // System.out.println("rA!");
108        if (endOfStream() && !hasData()) {
109            skipBoundary();           
110            return false;
111        }
112        return true;
113    }
114
115    /**
116     * @see java.io.InputStream#read()
117     */
118    @Override
119    public int read() throws IOException {
120        if (!readAllowed()) return -1;
121        for (;;) {
122            if (hasData()) {
123                return buffer.read();
124            } else if (endOfStream()) {
125                skipBoundary();           
126                return -1;
127            }
128            fillBuffer();
129        }
130    }
131   
132    @Override
133    public int read(byte[] b, int off, int len) throws IOException {
134        if (!readAllowed()) return -1;
135        fillBuffer();
136        if (!hasData()) {
137            return read(b, off, len);
138        }
139        int chunk = Math.min(len, limit - buffer.pos());
140        return buffer.read(b, off, chunk);
141    }
142
143    @Override
144    public int readLine(final ByteArrayBuffer dst) throws IOException {
145        if (dst == null) {
146            throw new IllegalArgumentException("Destination buffer may not be null");
147        }
148        if (!readAllowed()) return -1;
149       
150        int total = 0;
151        boolean found = false;
152        int bytesRead = 0;
153        while (!found) {
154            if (!hasData()) {
155                bytesRead = fillBuffer();
156                if (endOfStream() && !hasData()) {
157                    skipBoundary();
158                    bytesRead = -1;
159                    break;
160                }
161            }
162            int len = this.limit - this.buffer.pos();
163            int i = this.buffer.indexOf((byte)'\n', this.buffer.pos(), len);
164            int chunk;
165            if (i != -1) {
166                found = true;
167                chunk = i + 1 - this.buffer.pos();
168            } else {
169                chunk = len;
170            }
171            if (chunk > 0) {
172                dst.append(this.buffer.buf(), this.buffer.pos(), chunk);
173                this.buffer.skip(chunk);
174                total += chunk;
175            }
176        }
177        if (total == 0 && bytesRead == -1) {
178            return -1;
179        } else {
180            return total;
181        }
182    }
183   
184        private boolean endOfStream() {
185        return eof || atBoundary;
186    }
187   
188    private boolean hasData() {
189        return limit > buffer.pos() && limit <= buffer.limit();
190    }
191   
192    private int fillBuffer() throws IOException {
193        if (eof) {
194            return -1;
195        }
196        int bytesRead;
197        if (!hasData()) {
198            bytesRead = buffer.fillBuffer();
199            if (bytesRead == -1) {
200                eof = true;
201            }
202        } else {
203            bytesRead = 0;
204        }
205       
206       
207        int i = buffer.indexOf(boundary);
208        // NOTE this currently check only for LF. It doesn't check for canonical CRLF
209        // and neither for isolated CR. This will require updates according to MIME4J-60
210        while (i > buffer.pos() && buffer.charAt(i-1) != '\n') {
211            // skip the "fake" boundary (it does not contain LF or CR so we cannot have
212            // another boundary starting before this is complete.
213            i = i + boundary.length;
214            i = buffer.indexOf(boundary, i, buffer.limit() - i);
215        }
216        if (i != -1) {
217            limit = i;
218            atBoundary = true;
219            calculateBoundaryLen();
220        } else {
221            if (eof) {
222                limit = buffer.limit();
223            } else {
224                limit = buffer.limit() - (boundary.length + 1);
225                                          // \r\n + (boundary - one char)
226            }
227        }
228        return bytesRead;
229    }
230   
231    public boolean isEmptyStream() {
232        return initialLength == 0;
233    }
234   
235    public boolean isFullyConsumed() {
236        return completed && !buffer.hasBufferedData();
237    }
238   
239    private void calculateBoundaryLen() throws IOException {
240        boundaryLen = boundary.length;
241        int len = limit - buffer.pos();
242        if (len >= 0 && initialLength == -1) initialLength = len;
243        if (len > 0) {
244            if (buffer.charAt(limit - 1) == '\n') {
245                boundaryLen++;
246                limit--;
247            }
248        }
249        if (len > 1) {
250            if (buffer.charAt(limit - 1) == '\r') {
251                boundaryLen++;
252                limit--;
253            }
254        }
255    }
256   
257    private void skipBoundary() throws IOException {
258        if (!completed) {
259            completed = true;
260            buffer.skip(boundaryLen);
261            boolean checkForLastPart = true;
262            for (;;) {
263                if (buffer.length() > 1) {
264                    int ch1 = buffer.charAt(buffer.pos());
265                    int ch2 = buffer.charAt(buffer.pos() + 1);
266                   
267                    if (checkForLastPart) if (ch1 == '-' && ch2 == '-') {
268                        this.lastPart = true;
269                        buffer.skip(2);
270                        checkForLastPart = false;
271                        continue;
272                    }
273                   
274                    if (ch1 == '\r' && ch2 == '\n') {
275                        buffer.skip(2);
276                        break;
277                    } else if (ch1 == '\n') {
278                        buffer.skip(1);
279                        break;
280                    } else {
281                        // ignoring everything in a line starting with a boundary.
282                        buffer.skip(1);
283                    }
284                   
285                } else {
286                    if (eof) {
287                        break;
288                    }
289                    fillBuffer();
290                }
291            }
292        }
293    }
294   
295    public boolean isLastPart() {
296        return lastPart;       
297    }
298   
299    public boolean eof() {
300        return eof && !buffer.hasBufferedData();
301    }
302
303    @Override
304    public String toString() {
305        final StringBuilder buffer = new StringBuilder("MimeBoundaryInputStream, boundary ");
306        for (byte b : boundary) {
307            buffer.append((char) b);
308        }
309        return buffer.toString();
310    }
311
312        @Override
313        public boolean unread(ByteArrayBuffer buf) {
314                return false;
315        }
316}
Note: See TracBrowser for help on using the repository browser.