source: contrib/MailArchiver/sources/vendor/mime4j/apache-mime4j-0.7-SNAPSHOT-20110327.010440-17/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java @ 6785

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