1 package org
.apache
.james
.mime4j
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
6 import org
.apache
.james
.mime4j
.decoder
.Base64InputStream
;
7 import org
.apache
.james
.mime4j
.decoder
.QuotedPrintableInputStream
;
8 import org
.apache
.james
.mime4j
.util
.MimeUtil
;
10 public class MimeEntity
extends AbstractEntity
{
13 * Internal state, not exposed.
15 private static final int T_IN_BODYPART
= -2;
17 * Internal state, not exposed.
19 private static final int T_IN_MESSAGE
= -3;
21 private final RootInputStream rootStream
;
22 private final InputStream rawStream
;
23 private final InputBuffer inbuffer
;
25 private int recursionMode
;
26 private MimeBoundaryInputStream mimeStream
;
27 private BufferingInputStreamAdaptor dataStream
;
28 private boolean skipHeader
;
30 private byte[] tmpbuf
;
33 RootInputStream rootStream
,
34 InputStream rawStream
,
36 BodyDescriptor parent
,
39 boolean maximalBodyDescriptor
,
40 boolean strictParsing
) {
41 super(parent
, startState
, endState
, maximalBodyDescriptor
, strictParsing
);
42 this.rootStream
= rootStream
;
43 this.inbuffer
= inbuffer
;
44 this.rawStream
= rawStream
;
45 this.dataStream
= new BufferingInputStreamAdaptor(rawStream
);
46 this.skipHeader
= false;
50 RootInputStream rootStream
,
51 InputStream rawStream
,
53 BodyDescriptor parent
,
56 this(rootStream
, rawStream
, inbuffer
, parent
, startState
, endState
, false, false);
59 public int getRecursionMode() {
63 public void setRecursionMode(int recursionMode
) {
64 this.recursionMode
= recursionMode
;
67 public void skipHeader(String contentType
) {
68 if (state
!= EntityStates
.T_START_MESSAGE
) {
69 throw new IllegalStateException("Invalid state: " + stateToString(state
));
72 body
.addField("Content-Type", contentType
);
75 protected int getLineNumber() {
76 return rootStream
.getLineNumber();
79 protected BufferingInputStream
getDataStream() {
83 public EntityStateMachine
advance() throws IOException
, MimeException
{
85 case EntityStates
.T_START_MESSAGE
:
87 state
= EntityStates
.T_END_HEADER
;
89 state
= EntityStates
.T_START_HEADER
;
92 case EntityStates
.T_START_BODYPART
:
93 state
= EntityStates
.T_START_HEADER
;
95 case EntityStates
.T_START_HEADER
:
96 case EntityStates
.T_FIELD
:
97 state
= parseField() ? EntityStates
.T_FIELD
: EntityStates
.T_END_HEADER
;
99 case EntityStates
.T_END_HEADER
:
100 String mimeType
= body
.getMimeType();
101 if (recursionMode
== RecursionMode
.M_FLAT
) {
102 state
= EntityStates
.T_BODY
;
103 } else if (MimeUtil
.isMultipart(mimeType
)) {
104 state
= EntityStates
.T_START_MULTIPART
;
106 } else if (recursionMode
!= RecursionMode
.M_NO_RECURSE
107 && MimeUtil
.isMessage(mimeType
)) {
108 state
= T_IN_MESSAGE
;
109 return nextMessage();
111 state
= EntityStates
.T_BODY
;
114 case EntityStates
.T_START_MULTIPART
:
115 if (dataStream
.isUsed()) {
117 state
= EntityStates
.T_END_MULTIPART
;
120 state
= EntityStates
.T_PREAMBLE
;
123 case EntityStates
.T_PREAMBLE
:
125 if (mimeStream
.isLastPart()) {
127 state
= EntityStates
.T_END_MULTIPART
;
130 state
= T_IN_BODYPART
;
131 return nextMimeEntity();
136 if (mimeStream
.eof() && !mimeStream
.isLastPart()) {
137 monitor(Event
.MIME_BODY_PREMATURE_END
);
139 if (!mimeStream
.isLastPart()) {
141 state
= T_IN_BODYPART
;
142 return nextMimeEntity();
146 state
= EntityStates
.T_EPILOGUE
;
148 case EntityStates
.T_EPILOGUE
:
149 state
= EntityStates
.T_END_MULTIPART
;
151 case EntityStates
.T_BODY
:
152 case EntityStates
.T_END_MULTIPART
:
157 if (state
== endState
) {
158 state
= EntityStates
.T_END_OF_STREAM
;
161 throw new IllegalStateException("Invalid state: " + stateToString(state
));
166 private void createMimeStream() throws IOException
{
167 mimeStream
= new MimeBoundaryInputStream(inbuffer
, body
.getBoundary());
168 dataStream
= new BufferingInputStreamAdaptor(mimeStream
);
169 // If multipart message is embedded into another multipart message
170 // make sure to reset parent's mime stream
171 if (rawStream
instanceof BufferingInputStream
) {
172 ((BufferingInputStream
) rawStream
).reset();
176 private void clearMimeStream() {
178 dataStream
= new BufferingInputStreamAdaptor(rawStream
);
181 private void advanceToBoundary() throws IOException
{
182 if (!dataStream
.eof()) {
183 if (tmpbuf
== null) {
184 tmpbuf
= new byte[2048];
186 while (dataStream
.read(tmpbuf
)!= -1) {
191 private EntityStateMachine
nextMessage() {
192 String transferEncoding
= body
.getTransferEncoding();
193 InputStream instream
;
195 if (MimeUtil
.isBase64Encoding(transferEncoding
)) {
196 log
.debug("base64 encoded message/rfc822 detected");
197 instream
= new Base64InputStream(dataStream
);
198 buffer
= new InputBuffer(instream
, 4 * 1024);
199 } else if (MimeUtil
.isQuotedPrintableEncoded(transferEncoding
)) {
200 log
.debug("quoted-printable encoded message/rfc822 detected");
201 instream
= new QuotedPrintableInputStream(dataStream
);
202 buffer
= new InputBuffer(instream
, 4 * 1024);
204 instream
= dataStream
;
208 if (recursionMode
== RecursionMode
.M_RAW
) {
209 RawEntity message
= new RawEntity(instream
);
212 MimeEntity message
= new MimeEntity(
217 EntityStates
.T_START_MESSAGE
,
218 EntityStates
.T_END_MESSAGE
,
219 maximalBodyDescriptor
,
221 message
.setRecursionMode(recursionMode
);
226 private EntityStateMachine
nextMimeEntity() {
227 if (recursionMode
== RecursionMode
.M_RAW
) {
228 RawEntity message
= new RawEntity(mimeStream
);
231 MimeEntity mimeentity
= new MimeEntity(
236 EntityStates
.T_START_BODYPART
,
237 EntityStates
.T_END_BODYPART
,
238 maximalBodyDescriptor
,
240 mimeentity
.setRecursionMode(recursionMode
);
245 public InputStream
getContentStream() {
247 case EntityStates
.T_START_MULTIPART
:
248 case EntityStates
.T_PREAMBLE
:
249 case EntityStates
.T_EPILOGUE
:
250 case EntityStates
.T_BODY
:
251 return this.dataStream
;
253 throw new IllegalStateException("Invalid state: " + stateToString(state
));