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    }