Added new recursion mode.
[mime4j.git] / src / main / java / org / apache / james / mime4j / MimeEntity.java
blobd31a4533d5c6aa17db2ac059b61b1ffa865c5cd7
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 {
12 /**
13 * Internal state, not exposed.
15 private static final int T_IN_BODYPART = -2;
16 /**
17 * Internal state, not exposed.
19 private static final int T_IN_MESSAGE = -3;
21 private final InputBuffer inbuffer;
22 private final InputStream rawStream;
24 private int recursionMode;
25 private MimeBoundaryInputStream mimeStream;
26 private EOFSensitiveInputStream dataStream;
27 private boolean skipHeader;
29 public MimeEntity(
30 RootInputStream rootStream,
31 InputStream rawStream,
32 InputBuffer inbuffer,
33 BodyDescriptor parent,
34 int startState,
35 int endState,
36 boolean maximalBodyDescriptor,
37 boolean strictParsing) {
38 super(rootStream, parent, startState, endState, maximalBodyDescriptor, strictParsing);
39 this.inbuffer = inbuffer;
40 this.rawStream = rawStream;
41 this.dataStream = new EOFSensitiveInputStream(rawStream);
42 this.skipHeader = false;
45 public MimeEntity(
46 RootInputStream rootStream,
47 InputStream rawStream,
48 InputBuffer inbuffer,
49 BodyDescriptor parent,
50 int startState,
51 int endState) {
52 this(rootStream, rawStream, inbuffer, parent, startState, endState, false, false);
55 public int getRecursionMode() {
56 return recursionMode;
59 public void setRecursionMode(int recursionMode) {
60 this.recursionMode = recursionMode;
63 public void skipHeader(String contentType) {
64 if (state != EntityStates.T_START_MESSAGE) {
65 throw new IllegalStateException("Invalid state: " + stateToString(state));
67 skipHeader = true;
68 body.addField("Content-Type", contentType);
71 protected InputStream getDataStream() {
72 return dataStream;
75 public EntityStateMachine advance() throws IOException, MimeException {
76 switch (state) {
77 case EntityStates.T_START_MESSAGE:
78 if (skipHeader) {
79 state = EntityStates.T_END_HEADER;
80 } else {
81 state = EntityStates.T_START_HEADER;
83 break;
84 case EntityStates.T_START_BODYPART:
85 state = EntityStates.T_START_HEADER;
86 break;
87 case EntityStates.T_START_HEADER:
88 initHeaderParsing();
89 state = parseField() ? EntityStates.T_FIELD : EntityStates.T_END_HEADER;
90 break;
91 case EntityStates.T_FIELD:
92 state = parseField() ? EntityStates.T_FIELD : EntityStates.T_END_HEADER;
93 break;
94 case EntityStates.T_END_HEADER:
95 String mimeType = body.getMimeType();
96 if (recursionMode == RecursionMode.M_FLAT) {
97 state = EntityStates.T_BODY;
98 } else if (MimeUtil.isMultipart(mimeType)) {
99 state = EntityStates.T_START_MULTIPART;
100 clearMimeStream();
101 } else if (recursionMode != RecursionMode.M_NO_RECURSE
102 && MimeUtil.isMessage(mimeType)) {
103 state = T_IN_MESSAGE;
104 return nextMessage();
105 } else {
106 state = EntityStates.T_BODY;
108 break;
109 case EntityStates.T_START_MULTIPART:
110 if (dataStream.isUsed()) {
111 advanceToBoundary();
112 state = EntityStates.T_END_MULTIPART;
113 } else {
114 createMimeStream();
115 state = EntityStates.T_PREAMBLE;
117 break;
118 case EntityStates.T_PREAMBLE:
119 advanceToBoundary();
120 if (mimeStream.isLastPart()) {
121 clearMimeStream();
122 state = EntityStates.T_END_MULTIPART;
123 } else {
124 createMimeStream();
125 state = T_IN_BODYPART;
126 return nextMimeEntity();
128 break;
129 case T_IN_BODYPART:
130 advanceToBoundary();
131 if (mimeStream.eof() && !mimeStream.isLastPart()) {
132 monitor(Event.MIME_BODY_PREMATURE_END);
133 } else {
134 if (!mimeStream.isLastPart()) {
135 createMimeStream();
136 state = T_IN_BODYPART;
137 return nextMimeEntity();
140 clearMimeStream();
141 state = EntityStates.T_EPILOGUE;
142 break;
143 case EntityStates.T_EPILOGUE:
144 state = EntityStates.T_END_MULTIPART;
145 break;
146 case EntityStates.T_BODY:
147 case EntityStates.T_END_MULTIPART:
148 case T_IN_MESSAGE:
149 state = endState;
150 break;
151 default:
152 if (state == endState) {
153 state = EntityStates.T_END_OF_STREAM;
154 break;
156 throw new IllegalStateException("Invalid state: " + stateToString(state));
158 return null;
161 private void createMimeStream() throws IOException {
162 mimeStream = new MimeBoundaryInputStream(inbuffer, body.getBoundary());
163 dataStream = new EOFSensitiveInputStream(mimeStream);
166 private void clearMimeStream() {
167 mimeStream = null;
168 dataStream = new EOFSensitiveInputStream(rawStream);
171 private void advanceToBoundary() throws IOException {
172 if (!dataStream.eof()) {
173 byte[] tmp = new byte[2048];
174 while (dataStream.read(tmp)!= -1) {
179 private EntityStateMachine nextMessage() {
180 String transferEncoding = body.getTransferEncoding();
181 InputStream instream;
182 if (MimeUtil.isBase64Encoding(transferEncoding)) {
183 log.debug("base64 encoded message/rfc822 detected");
184 instream = new EOLConvertingInputStream(
185 new Base64InputStream(mimeStream));
186 } else if (MimeUtil.isQuotedPrintableEncoded(transferEncoding)) {
187 log.debug("quoted-printable encoded message/rfc822 detected");
188 instream = new EOLConvertingInputStream(
189 new QuotedPrintableInputStream(mimeStream));
190 } else {
191 instream = dataStream;
194 if (recursionMode == RecursionMode.M_RAW) {
195 RawEntity message = new RawEntity(instream);
196 return message;
197 } else {
198 MimeEntity message = new MimeEntity(
199 rootStream,
200 instream,
201 inbuffer,
202 body,
203 EntityStates.T_START_MESSAGE,
204 EntityStates.T_END_MESSAGE,
205 maximalBodyDescriptor,
206 strictParsing);
207 message.setRecursionMode(recursionMode);
208 return message;
212 private EntityStateMachine nextMimeEntity() {
213 if (recursionMode == RecursionMode.M_RAW) {
214 RawEntity message = new RawEntity(mimeStream);
215 return message;
216 } else {
217 MimeEntity mimeentity = new MimeEntity(
218 rootStream,
219 mimeStream,
220 inbuffer,
221 body,
222 EntityStates.T_START_BODYPART,
223 EntityStates.T_END_BODYPART,
224 maximalBodyDescriptor,
225 strictParsing);
226 mimeentity.setRecursionMode(recursionMode);
227 return mimeentity;
231 public InputStream getContentStream() {
232 switch (state) {
233 case EntityStates.T_START_MULTIPART:
234 case EntityStates.T_PREAMBLE:
235 case EntityStates.T_EPILOGUE:
236 case EntityStates.T_BODY:
237 return this.dataStream;
238 default:
239 throw new IllegalStateException("Invalid state: " + stateToString(state));