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

Revision 6785, 11.5 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.stream;
21
22import java.io.IOException;
23import java.io.InputStream;
24
25import org.apache.james.mime4j.MimeException;
26import org.apache.james.mime4j.codec.Base64InputStream;
27import org.apache.james.mime4j.codec.DecodeMonitor;
28import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
29import org.apache.james.mime4j.io.BufferedLineReaderInputStream;
30import org.apache.james.mime4j.io.LimitedInputStream;
31import org.apache.james.mime4j.io.LineNumberSource;
32import org.apache.james.mime4j.io.LineReaderInputStream;
33import org.apache.james.mime4j.io.LineReaderInputStreamAdaptor;
34import org.apache.james.mime4j.io.MimeBoundaryInputStream;
35import org.apache.james.mime4j.util.MimeUtil;
36
37class MimeEntity extends AbstractEntity {
38
39    private final LineNumberSource lineSource;
40    private final BufferedLineReaderInputStream inbuffer;
41
42    private RecursionMode recursionMode;
43    private MimeBoundaryInputStream currentMimePartStream;
44    private LineReaderInputStreamAdaptor dataStream;
45
46    private byte[] tmpbuf;
47
48    MimeEntity(
49            LineNumberSource lineSource,
50            InputStream instream,
51            MimeEntityConfig config,
52            EntityState startState,
53            EntityState endState,
54            DecodeMonitor monitor,
55            FieldBuilder fieldBuilder,
56            MutableBodyDescriptor body) {
57        super(config, startState, endState, monitor, fieldBuilder, body);
58        this.lineSource = lineSource;
59        this.inbuffer = new BufferedLineReaderInputStream(
60                instream,
61                4 * 1024,
62                config.getMaxLineLen());
63        this.dataStream = new LineReaderInputStreamAdaptor(
64                inbuffer,
65                config.getMaxLineLen());
66    }
67
68    MimeEntity(
69            LineNumberSource lineSource,
70            InputStream instream,
71            MimeEntityConfig config,
72            EntityState startState,
73            EntityState endState,
74            MutableBodyDescriptor body) {
75        this(lineSource, instream, config, startState, endState,
76                config.isStrictParsing() ? DecodeMonitor.STRICT : DecodeMonitor.SILENT,
77                new DefaultFieldBuilder(config.getMaxHeaderLen()), body);
78    }
79
80    MimeEntity(
81            LineNumberSource lineSource,
82            InputStream instream,
83            MimeEntityConfig config,
84            MutableBodyDescriptor body) {
85        this(lineSource, instream, config,
86                EntityState.T_START_MESSAGE, EntityState.T_END_MESSAGE,
87                config.isStrictParsing() ? DecodeMonitor.STRICT : DecodeMonitor.SILENT,
88                new DefaultFieldBuilder(config.getMaxHeaderLen()), body);
89    }
90
91    MimeEntity(
92            LineNumberSource lineSource,
93            InputStream instream,
94            FieldBuilder fieldBuilder,
95            MutableBodyDescriptor body) {
96        this(lineSource, instream, new MimeEntityConfig(),
97                EntityState.T_START_MESSAGE, EntityState.T_END_MESSAGE,
98                DecodeMonitor.SILENT,
99                fieldBuilder, body);
100    }
101
102    MimeEntity(
103            LineNumberSource lineSource,
104            InputStream instream,
105            MutableBodyDescriptor body) {
106        this(lineSource, instream, new MimeEntityConfig(),
107                EntityState.T_START_MESSAGE, EntityState.T_END_MESSAGE,
108                DecodeMonitor.SILENT,
109                new DefaultFieldBuilder(-1), body);
110    }
111
112    public RecursionMode getRecursionMode() {
113        return recursionMode;
114    }
115
116    public void setRecursionMode(RecursionMode recursionMode) {
117        this.recursionMode = recursionMode;
118    }
119
120    public void stop() {
121        this.inbuffer.truncate();
122    }
123
124    @Override
125    protected int getLineNumber() {
126        if (lineSource == null)
127            return -1;
128        else
129            return lineSource.getLineNumber();
130    }
131
132    @Override
133    protected LineReaderInputStream getDataStream() {
134        return dataStream;
135    }
136
137    public EntityStateMachine advance() throws IOException, MimeException {
138        switch (state) {
139        case T_START_MESSAGE:
140            state = EntityState.T_START_HEADER;
141            break;
142        case T_START_BODYPART:
143            state = EntityState.T_START_HEADER;
144            break;
145        case T_START_HEADER:
146        case T_FIELD:
147            state = nextField() ? EntityState.T_FIELD : EntityState.T_END_HEADER;
148            break;
149        case T_END_HEADER:
150            String mimeType = body.getMimeType();
151            if (recursionMode == RecursionMode.M_FLAT) {
152                state = EntityState.T_BODY;
153            } else if (MimeUtil.isMultipart(mimeType)) {
154                state = EntityState.T_START_MULTIPART;
155                clearMimePartStream();
156            } else if (recursionMode != RecursionMode.M_NO_RECURSE
157                    && MimeUtil.isMessage(mimeType)) {
158                state = EntityState.T_BODY;
159                return nextMessage();
160            } else {
161                state = EntityState.T_BODY;
162            }
163            break;
164        case T_START_MULTIPART:
165            if (dataStream.isUsed()) {
166                advanceToBoundary();
167                state = EntityState.T_END_MULTIPART;
168                break;
169            } else {
170                createMimePartStream();
171                state = EntityState.T_PREAMBLE;
172
173                boolean empty = currentMimePartStream.isEmptyStream();
174                if (!empty) break;
175                // continue to next state
176            }
177        case T_PREAMBLE:
178            // removed specific code. Fallback to T_IN_BODYPART that
179            // better handle missing parts.
180            // Removed the T_IN_BODYPART state (always use T_PREAMBLE)
181            advanceToBoundary();
182            if (currentMimePartStream.eof() && !currentMimePartStream.isLastPart()) {
183                monitor(Event.MIME_BODY_PREMATURE_END);
184            } else {
185                if (!currentMimePartStream.isLastPart()) {
186                    clearMimePartStream();
187                    createMimePartStream();
188                    return nextMimeEntity();
189                }
190            }
191            boolean empty = currentMimePartStream.isFullyConsumed();
192            clearMimePartStream();
193            state = EntityState.T_EPILOGUE;
194            if (!empty) break;
195            // continue to next state
196        case T_EPILOGUE:
197            state = EntityState.T_END_MULTIPART;
198            break;
199        case T_BODY:
200        case T_END_MULTIPART:
201            state = endState;
202            break;
203        default:
204            if (state == endState) {
205                state = EntityState.T_END_OF_STREAM;
206                break;
207            }
208            throw new IllegalStateException("Invalid state: " + stateToString(state));
209        }
210        return null;
211    }
212
213    private void createMimePartStream() throws MimeException, IOException {
214        String boundary = body.getBoundary();
215        try {
216            currentMimePartStream = new MimeBoundaryInputStream(inbuffer, boundary);
217        } catch (IllegalArgumentException e) {
218            // thrown when boundary is too long
219            throw new MimeException(e.getMessage(), e);
220        }
221        dataStream = new LineReaderInputStreamAdaptor(
222                currentMimePartStream,
223                config.getMaxLineLen());
224    }
225
226    private void clearMimePartStream() {
227        currentMimePartStream = null;
228        dataStream = new LineReaderInputStreamAdaptor(
229                inbuffer,
230                config.getMaxLineLen());
231    }
232
233    private void advanceToBoundary() throws IOException {
234        if (!dataStream.eof()) {
235            if (tmpbuf == null) {
236                tmpbuf = new byte[2048];
237            }
238            InputStream instream = getLimitedContentStream();
239            while (instream.read(tmpbuf)!= -1) {
240            }
241        }
242    }
243
244    private EntityStateMachine nextMessage() {
245        // optimize nesting of streams returning the "lower" stream instead of
246        // always return dataStream (that would add a LineReaderInputStreamAdaptor in the chain)
247        InputStream instream = currentMimePartStream != null ? currentMimePartStream : inbuffer;
248        instream = decodedStream(instream);
249        return nextMimeEntity(EntityState.T_START_MESSAGE, EntityState.T_END_MESSAGE, instream);
250    }
251
252    private InputStream decodedStream(InputStream instream) {
253        String transferEncoding = body.getTransferEncoding();
254        if (MimeUtil.isBase64Encoding(transferEncoding)) {
255            instream = new Base64InputStream(instream, monitor);
256        } else if (MimeUtil.isQuotedPrintableEncoded(transferEncoding)) {
257            instream = new QuotedPrintableInputStream(instream, monitor);
258        }
259        return instream;
260    }
261
262    private EntityStateMachine nextMimeEntity() {
263        return nextMimeEntity(EntityState.T_START_BODYPART, EntityState.T_END_BODYPART, currentMimePartStream);
264    }
265
266    private EntityStateMachine nextMimeEntity(EntityState startState, EntityState endState, InputStream instream) {
267        if (recursionMode == RecursionMode.M_RAW) {
268            RawEntity message = new RawEntity(instream);
269            return message;
270        } else {
271            MimeEntity mimeentity = new MimeEntity(
272                    lineSource,
273                    instream,
274                    config,
275                    startState,
276                    endState,
277                    monitor,
278                    fieldBuilder,
279                    body.newChild());
280            mimeentity.setRecursionMode(recursionMode);
281            return mimeentity;
282        }
283    }
284
285    private InputStream getLimitedContentStream() {
286        long maxContentLimit = config.getMaxContentLen();
287        if (maxContentLimit >= 0) {
288            return new LimitedInputStream(dataStream, maxContentLimit);
289        } else {
290            return dataStream;
291        }
292    }
293
294    /**
295     * @see org.apache.james.mime4j.stream.EntityStateMachine#getContentStream()
296     */
297    public InputStream getContentStream() {
298        switch (state) {
299        case T_START_MULTIPART:
300        case T_PREAMBLE:
301        case T_EPILOGUE:
302        case T_BODY:
303            return getLimitedContentStream();
304        default:
305            throw new IllegalStateException("Invalid state: " + stateToString(state));
306        }
307    }
308
309    /**
310     * @see org.apache.james.mime4j.stream.EntityStateMachine#getDecodedContentStream()
311     */
312    public InputStream getDecodedContentStream() throws IllegalStateException {
313        return decodedStream(getContentStream());
314    }
315
316}
Note: See TracBrowser for help on using the repository browser.