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 }