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.ArrayList;
021 import java.util.HashMap;
022 import java.util.List;
023 import java.util.Map;
024 import java.util.TreeMap;
025
026 import org.apache.commons.collections.CollectionUtils;
027 import org.apache.commons.collections.MapUtils;
028 import org.apache.james.mime4j.MimeException;
029 import org.apache.james.mime4j.descriptor.MaximalBodyDescriptor;
030 import org.apache.james.mime4j.message.Header;
031 import org.apache.james.mime4j.parser.Field;
032 import org.apache.james.mime4j.parser.MimeTokenStream;
033 import org.apache.james.mime4j.parser.RecursionMode;
034
035 import com.hs.mail.io.CountingInputStream;
036
037 /**
038 *
039 * @author Won Chul Doh
040 * @since Mar 8, 2010
041 *
042 */
043 public class BodyStructureBuilder {
044
045 private EnvelopeBuilder envelopeBuilder;
046
047 public BodyStructureBuilder(EnvelopeBuilder envelopeBuilder) {
048 this.envelopeBuilder = envelopeBuilder;
049 }
050
051 public MimeDescriptor build(InputStream is) throws IOException,
052 MimeException {
053 MimeTokenStream parser = MimeParser.createMaximalDescriptorParser();
054 parser.parse(is);
055 parser.setRecursionMode(RecursionMode.M_NO_RECURSE);
056 return createDescriptor(parser);
057 }
058
059 private MimeDescriptor createDescriptor(MimeTokenStream parser)
060 throws IOException, MimeException {
061 int next = parser.next();
062 Header header = new Header();
063 while (next != MimeTokenStream.T_BODY
064 && next != MimeTokenStream.T_END_OF_STREAM
065 && next != MimeTokenStream.T_START_MULTIPART) {
066 if (next == MimeTokenStream.T_FIELD) {
067 header.addField(parser.getField());
068 }
069 next = parser.next();
070 }
071
072 switch (next) {
073 case MimeTokenStream.T_BODY:
074 return simplePartDescriptor(parser, header);
075 case MimeTokenStream.T_START_MULTIPART:
076 return compositePartDescriptor(parser, header);
077 case MimeTokenStream.T_END_OF_STREAM:
078 throw new MimeException("Premature end of stream");
079 default:
080 throw new MimeException("Unexpected parse state");
081 }
082 }
083
084 private MimeDescriptor compositePartDescriptor(
085 MimeTokenStream parser, Header header) throws IOException,
086 MimeException {
087 MaximalBodyDescriptor descriptor = (MaximalBodyDescriptor) parser
088 .getBodyDescriptor();
089 MimeDescriptor mimeDescriptor = createDescriptor(0, 0, descriptor,
090 null, null);
091 int next = parser.next();
092 while (next != MimeTokenStream.T_END_MULTIPART
093 && next != MimeTokenStream.T_END_OF_STREAM) {
094 if (next == MimeTokenStream.T_START_BODYPART) {
095 mimeDescriptor.addPart(createDescriptor(parser));
096 }
097 next = parser.next();
098 }
099 return mimeDescriptor;
100 }
101
102 private MimeDescriptor simplePartDescriptor(MimeTokenStream parser,
103 Header header) throws IOException, MimeException {
104 MaximalBodyDescriptor descriptor = (MaximalBodyDescriptor) parser
105 .getBodyDescriptor();
106 if ("message/rfc822".equals(descriptor.getMimeType())) {
107 CountingInputStream stream = new CountingInputStream(parser
108 .getDecodedInputStream());
109 MimeDescriptor embeddedMessageDescriptor = build(stream);
110 Envelope envelope = createEnvelope(header);
111 int octetCount = stream.getOctetCount();
112 int lineCount = stream.getLineCount();
113 return createDescriptor(octetCount, lineCount, descriptor,
114 embeddedMessageDescriptor, envelope);
115 } else {
116 CountingInputStream stream = new CountingInputStream(parser
117 .getInputStream());
118 stream.readAll();
119 int bodyOctets = stream.getOctetCount();
120 int lines = stream.getLineCount();
121 return createDescriptor(bodyOctets, lines, descriptor, null, null);
122 }
123 }
124
125 private MimeDescriptor createDescriptor(long bodyOctets, long lines,
126 MaximalBodyDescriptor descriptor,
127 MimeDescriptor embeddedMessageDescriptor, Envelope envelope) {
128 Map<String, String> parameters = new TreeMap<String, String>(descriptor
129 .getContentTypeParameters());
130 String charset = descriptor.getCharset();
131 if (charset == null) {
132 if ("TEXT".equalsIgnoreCase(descriptor.getMediaType())) {
133 parameters.put("charset", "us-ascii");
134 }
135 } else {
136 parameters.put("charset", charset);
137 }
138 String boundary = descriptor.getBoundary();
139 if (boundary != null) {
140 parameters.put("boundary", boundary);
141 }
142 MimeDescriptor mimeDescriptor = new MimeDescriptor(bodyOctets, lines,
143 descriptor.getMediaType(),
144 descriptor.getSubType(),
145 descriptor.getContentId(),
146 descriptor.getContentDescription(),
147 descriptor.getTransferEncoding(),
148 createParameters(parameters),
149 descriptor.getContentDispositionType(),
150 createParameters(descriptor.getContentDispositionParameters()),
151 descriptor.getContentLanguage(),
152 descriptor.getContentLocation(),
153 descriptor.getContentMD5Raw(),
154 new ArrayList<MimeDescriptor>(),
155 embeddedMessageDescriptor,
156 envelope);
157 return mimeDescriptor;
158 }
159
160 private List<String> createParameters(Map<String, String> parameters) {
161 if (MapUtils.isNotEmpty(parameters)) {
162 List<String> results = new ArrayList<String>();
163 for (Map.Entry<String, String> entry : parameters.entrySet()) {
164 results.add(entry.getKey());
165 results.add(entry.getValue());
166 }
167 return results;
168 } else {
169 return null;
170 }
171 }
172
173 private Envelope createEnvelope(Header h) {
174 Map<String, String> header = new HashMap<String, String>();
175 List<Field> fields = h.getFields();
176 if (!CollectionUtils.isEmpty(fields)) {
177 for (Field field : fields) {
178 header.put(field.getName(), field.getBody());
179 }
180 }
181 return envelopeBuilder.build(header);
182 }
183
184 }