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    }