source: contrib/MailArchiver/sources/vendor/mime4j/apache-mime4j-0.7-SNAPSHOT-20110327.010440-17/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
62        if (inbuffer.capacity() < boundary.length() * 2) {
63            throw new IllegalArgumentException("Boundary is too long");
64        }
65        this.buffer = inbuffer;
66        this.eof = false;
67        this.limit = -1;
68        this.atBoundary = false;
69        this.boundaryLen = 0;
70        this.lastPart = false;
71        this.initialLength = -1;
72        this.completed = false;
73       
74        this.boundary = new byte[boundary.length() + 2];
75        this.boundary[0] = (byte) '-';
76        this.boundary[1] = (byte) '-';
77        for (int i = 0; i < boundary.length(); i++) {
78            byte ch = (byte) boundary.charAt(i);
79            if (ch == '\r' || ch == '\n') {
80                throw new IllegalArgumentException("Boundary may not contain CR or LF");
81            }
82            this.boundary[i + 2] = ch;
83        }
84       
85        fillBuffer();
86    }
87
88    /**
89     * Closes the underlying stream.
90     *
91     * @throws IOException on I/O errors.
92     */
93    @Override
94    public void close() throws IOException {
95    }
96
97    /**
98     * @see java.io.InputStream#markSupported()
99     */
100    @Override
101    public boolean markSupported() {
102        return false;
103    }
104   
105    public boolean readAllowed() throws IOException {
106        if (completed) {
107            return false;
108        }
109        // System.out.println("rA!");
110        if (endOfStream() && !hasData()) {
111            skipBoundary();           
112            return false;
113        }
114        return true;
115    }
116
117    /**
118     * @see java.io.InputStream#read()
119     */
120    @Override
121    public int read() throws IOException {
122        if (!readAllowed()) return -1;
123        for (;;) {
124            if (hasData()) {
125                return buffer.read();
126            } else if (endOfStream()) {
127                skipBoundary();           
128                return -1;
129            }
130            fillBuffer();
131        }
132    }
133   
134    @Override
135    public int read(byte[] b, int off, int len) throws IOException {
136        if (!readAllowed()) return -1;
137        fillBuffer();
138        if (!hasData()) {
139            return read(b, off, len);
140        }
141        int chunk = Math.min(len, limit - buffer.pos());
142        return buffer.read(b, off, chunk);
143    }
144
145    @Override
146    public int readLine(final ByteArrayBuffer dst) throws IOException {
147        if (dst == null) {
148            throw new IllegalArgumentException("Destination buffer may not be null");
149        }
150        if (!readAllowed()) return -1;
151       
152        int total = 0;
153        boolean found = false;
154        int bytesRead = 0;
155        while (!found) {
156            if (!hasData()) {
157                bytesRead = fillBuffer();
158                if (endOfStream() && !hasData()) {
159                    skipBoundary();
160                    bytesRead = -1;
161                    break;
162                }
163            }
164            int len = this.limit - this.buffer.pos();
165            int i = this.buffer.indexOf((byte)'\n', this.buffer.pos(), len);
166            int chunk;
167            if (i != -1) {
168                found = true;
169                chunk = i + 1 - this.buffer.pos();
170            } else {
171                chunk = len;
172            }
173            if (chunk > 0) {
174                dst.append(this.buffer.buf(), this.buffer.pos(), chunk);
175                this.buffer.skip(chunk);
176                total += chunk;
177            }
178        }
179        if (total == 0 && bytesRead == -1) {
180            return -1;
181        } else {
182            return total;
183        }
184    }
185   
186        private boolean endOfStream() {
187        return eof || atBoundary;
188    }
189   
190    private boolean hasData() {
191        return limit > buffer.pos() && limit <= buffer.limit();
192    }
193   
194    private int fillBuffer() throws IOException {
195        if (eof) {
196            return -1;
197        }
198        int bytesRead;
199        if (!hasData()) {
200            bytesRead = buffer.fillBuffer();
201            if (bytesRead == -1) {
202                eof = true;
203            }
204        } else {
205            bytesRead = 0;
206        }
207       
208       
209        int i = buffer.indexOf(boundary);
210        // NOTE this currently check only for LF. It doesn't check for canonical CRLF
211        // and neither for isolated CR. This will require updates according to MIME4J-60
212        while (i > buffer.pos() && buffer.charAt(i-1) != '\n') {
213            // skip the "fake" boundary (it does not contain LF or CR so we cannot have
214            // another boundary starting before this is complete.
215            i = i + boundary.length;
216            i = buffer.indexOf(boundary, i, buffer.limit() - i);
217        }
218        if (i != -1) {
219            limit = i;
220            atBoundary = true;
221            calculateBoundaryLen();
222        } else {
223            if (eof) {
224                limit = buffer.limit();
225            } else {
226                limit = buffer.limit() - (boundary.length + 1);
227                                          // \r\n + (boundary - one char)
228            }
229        }
230        return bytesRead;
231    }
232   
233    public boolean isEmptyStream() {
234        return initialLength == 0;
235    }
236   
237    private void calculateBoundaryLen() throws IOException {
238        boundaryLen = boundary.length;
239        int len = limit - buffer.pos();
240        if (len >= 0 && initialLength == -1) initialLength = len;
241        if (len > 0) {
242            if (buffer.charAt(limit - 1) == '\n') {
243                boundaryLen++;
244                limit--;
245            }
246        }
247        if (len > 1) {
248            if (buffer.charAt(limit - 1) == '\r') {
249                boundaryLen++;
250                limit--;
251            }
252        }
253    }
254   
255    private void skipBoundary() throws IOException {
256        if (!completed) {
257            completed = true;
258            buffer.skip(boundaryLen);
259            boolean checkForLastPart = true;
260            for (;;) {
261                if (buffer.length() > 1) {
262                    int ch1 = buffer.charAt(buffer.pos());
263                    int ch2 = buffer.charAt(buffer.pos() + 1);
264                   
265                    if (checkForLastPart) if (ch1 == '-' && ch2 == '-') {
266                        this.lastPart = true;
267                        buffer.skip(2);
268                        checkForLastPart = false;
269                        continue;
270                    }
271                   
272                    if (ch1 == '\r' && ch2 == '\n') {
273                        buffer.skip(2);
274                        break;
275                    } else if (ch1 == '\n') {
276                        buffer.skip(1);
277                        break;
278                    } else {
279                        // ignoring everything in a line starting with a boundary.
280                        buffer.skip(1);
281                    }
282                   
283                } else {
284                    if (eof) {
285                        break;
286                    }
287                    fillBuffer();
288                }
289            }
290        }
291    }
292   
293    public boolean isLastPart() {
294        return lastPart;       
295    }
296   
297    public boolean eof() {
298        return eof && !buffer.hasBufferedData();
299    }
300
301    @Override
302    public String toString() {
303        final StringBuilder buffer = new StringBuilder("MimeBoundaryInputStream, boundary ");
304        for (byte b : boundary) {
305            buffer.append((char) b);
306        }
307        return buffer.toString();
308    }
309
310        @Override
311        public boolean unread(ByteArrayBuffer buf) {
312                return false;
313        }
314}
Note: See TracBrowser for help on using the repository browser.