001 /*
002 * Copyright 2010 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package com.hs.mail.imap.processor.fetch;
017
018 import java.io.IOException;
019 import java.io.InputStream;
020 import java.util.HashMap;
021 import java.util.Map;
022
023 import org.apache.commons.io.IOUtils;
024 import org.apache.james.mime4j.MimeException;
025 import org.apache.james.mime4j.parser.Field;
026 import org.apache.james.mime4j.parser.MimeTokenStream;
027
028 /**
029 *
030 * @author Won Chul Doh
031 * @since Mar 8, 2010
032 *
033 */
034 public class PartContentBuilder {
035
036 public static final byte[] EMPTY = {};
037
038 private MimeTokenStream parser;
039 private boolean topLevel = true;
040
041 public PartContentBuilder() {
042 parser = MimeParser.createDefaultMimeParser();
043 }
044
045 public void build(InputStream is, int[] path) {
046 try {
047 parser.setRecursionMode(MimeTokenStream.M_RECURSE);
048 parser.parse(is);
049 topLevel = true;
050 if (path != null) {
051 for (int i = 0; i < path.length; i++) {
052 to(path[i]);
053 }
054 }
055 } catch (Exception ex) {
056 }
057 }
058
059 private void skipToStartOfInner(int position) throws IOException,
060 MimeException {
061 final int state = parser.next();
062 switch (state) {
063 case MimeTokenStream.T_START_MULTIPART:
064 break;
065 case MimeTokenStream.T_START_MESSAGE:
066 break;
067 case MimeTokenStream.T_END_OF_STREAM:
068 throw new PartNotFoundException(position);
069 case MimeTokenStream.T_END_BODYPART:
070 throw new PartNotFoundException(position);
071 default:
072 skipToStartOfInner(position);
073 }
074 }
075
076 private void to(int position) throws IOException, MimeException {
077 try {
078 if (topLevel) {
079 topLevel = false;
080 } else {
081 skipToStartOfInner(position);
082 }
083 for (int count = 0; count < position;) {
084 int state = parser.next();
085 if (state == MimeTokenStream.T_START_BODYPART) {
086 count++;
087 } else if (state == MimeTokenStream.T_BODY && position == 1) {
088 count++;
089 } else if (state == MimeTokenStream.T_START_MULTIPART) {
090 if (count > 0 && count < position) {
091 ignore();
092 }
093 } else if (state == MimeTokenStream.T_END_OF_STREAM) {
094 throw new PartNotFoundException(position);
095 }
096 }
097 } catch (IllegalStateException e) {
098 throw new PartNotFoundException(position, e);
099 }
100 }
101
102 private void ignore() throws IOException, MimeException {
103 for (int state = parser.next(); state != MimeTokenStream.T_END_MULTIPART; state = parser
104 .next()) {
105 if (state == MimeTokenStream.T_START_MULTIPART) {
106 ignore();
107 break;
108 } else if (state == MimeTokenStream.T_END_OF_STREAM) {
109 throw new MimeException("Premature end of stream");
110 }
111 }
112 }
113
114 public Map<String, String> getMimeHeader() throws IOException, MimeException {
115 Map<String, String> header = new HashMap<String, String>();
116 for (int state = parser.getState(); state != MimeTokenStream.T_END_HEADER;
117 state = parser.next()) {
118 if (state == MimeTokenStream.T_FIELD) {
119 Field field = parser.getField();
120 header.put(field.getName(), field.getBody());
121 } else if (state == MimeTokenStream.T_END_OF_STREAM) {
122 throw new MimeException("Premature end of stream");
123 }
124 }
125 return header;
126 }
127
128 public Map<String, String> getMessageHeader() throws IOException, MimeException {
129 advanceToMessage();
130 return getMimeHeader();
131 }
132
133 private void advanceToMessage() throws IOException, MimeException {
134 for (int state = parser.getState(); state != MimeTokenStream.T_START_MESSAGE;
135 state = parser.next()) {
136 if (state == MimeTokenStream.T_END_OF_STREAM) {
137 throw new MimeException("Premature end of stream");
138 }
139 }
140 }
141
142 public byte[] getMimeBodyContent() throws IOException, MimeException {
143 parser.setRecursionMode(MimeTokenStream.M_FLAT);
144 for (int state = parser.getState(); state != MimeTokenStream.T_BODY
145 && state != MimeTokenStream.T_START_MULTIPART;
146 state = parser.next()) {
147 if (state == MimeTokenStream.T_END_OF_STREAM) {
148 return EMPTY;
149 }
150 }
151 return IOUtils.toByteArray(parser.getInputStream());
152 }
153
154 public byte[] getMessageBodyContent() throws IOException, MimeException {
155 advanceToMessageBody();
156 return getMimeBodyContent();
157 }
158
159 private void advanceToMessageBody() throws IOException, MimeException {
160 for (int state = parser.getState(); state != MimeTokenStream.T_BODY;
161 state = parser.next()) {
162 if (state == MimeTokenStream.T_END_OF_STREAM) {
163 throw new MimeException("Premature end of stream");
164 }
165 }
166 }
167
168 public class PartNotFoundException extends MimeException {
169
170 private static final long serialVersionUID = 1L;
171 private final int position;
172
173 public PartNotFoundException(int position) {
174 this(position, null);
175 }
176
177 public PartNotFoundException(int position, Exception e) {
178 super("Part " + position + " not found.", e);
179 this.position = position;
180 }
181
182 public final int getPosition() {
183 return position;
184 }
185
186 }
187
188 }