001    /****************************************************************
002     * Licensed to the Apache Software Foundation (ASF) under one   *
003     * or more contributor license agreements.  See the NOTICE file *
004     * distributed with this work for additional information        *
005     * regarding copyright ownership.  The ASF licenses this file   *
006     * to you under the Apache License, Version 2.0 (the            *
007     * "License"); you may not use this file except in compliance   *
008     * with the License.  You may obtain a copy of the License at   *
009     *                                                              *
010     *   http://www.apache.org/licenses/LICENSE-2.0                 *
011     *                                                              *
012     * Unless required by applicable law or agreed to in writing,   *
013     * software distributed under the License is distributed on an  *
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015     * KIND, either express or implied.  See the License for the    *
016     * specific language governing permissions and limitations      *
017     * under the License.                                           *
018     ****************************************************************/
019    
020    package org.apache.james.mime4j.message;
021    
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.util.Stack;
025    
026    import org.apache.james.mime4j.MimeException;
027    import org.apache.james.mime4j.codec.Base64InputStream;
028    import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
029    import org.apache.james.mime4j.descriptor.BodyDescriptor;
030    import org.apache.james.mime4j.field.AbstractField;
031    import org.apache.james.mime4j.parser.ContentHandler;
032    import org.apache.james.mime4j.parser.Field;
033    import org.apache.james.mime4j.parser.MimeStreamParser;
034    import org.apache.james.mime4j.storage.StorageProvider;
035    import org.apache.james.mime4j.util.ByteArrayBuffer;
036    import org.apache.james.mime4j.util.ByteSequence;
037    import org.apache.james.mime4j.util.MimeUtil;
038    
039    /**
040     * A <code>ContentHandler</code> for building an <code>Entity</code> to be
041     * used in conjunction with a {@link MimeStreamParser}.
042     */
043    public class MessageBuilder implements ContentHandler {
044    
045        private final Entity entity;
046        private final BodyFactory bodyFactory;
047        private Stack<Object> stack = new Stack<Object>();
048        
049        public MessageBuilder(Entity entity) {
050            this.entity = entity;
051            this.bodyFactory = new BodyFactory();
052        }
053        
054        public MessageBuilder(Entity entity, StorageProvider storageProvider) {
055            this.entity = entity;
056            this.bodyFactory = new BodyFactory(storageProvider);
057        }
058        
059        private void expect(Class<?> c) {
060            if (!c.isInstance(stack.peek())) {
061                throw new IllegalStateException("Internal stack error: "
062                        + "Expected '" + c.getName() + "' found '"
063                        + stack.peek().getClass().getName() + "'");
064            }
065        }
066        
067        /**
068         * @see org.apache.james.mime4j.parser.ContentHandler#startMessage()
069         */
070        public void startMessage() throws MimeException {
071            if (stack.isEmpty()) {
072                stack.push(this.entity);
073            } else {
074                expect(Entity.class);
075                Message m = new Message();
076                ((Entity) stack.peek()).setBody(m);
077                stack.push(m);
078            }
079        }
080        
081        /**
082         * @see org.apache.james.mime4j.parser.ContentHandler#endMessage()
083         */
084        public void endMessage() throws MimeException {
085            expect(Message.class);
086            stack.pop();
087        }
088        
089        /**
090         * @see org.apache.james.mime4j.parser.ContentHandler#startHeader()
091         */
092        public void startHeader() throws MimeException {
093            stack.push(new Header());
094        }
095        
096        /**
097         * @see org.apache.james.mime4j.parser.ContentHandler#field(Field)
098         */
099        public void field(Field field) throws MimeException {
100            expect(Header.class);
101            Field parsedField = AbstractField.parse(field.getRaw()); 
102            ((Header) stack.peek()).addField(parsedField);
103        }
104        
105        /**
106         * @see org.apache.james.mime4j.parser.ContentHandler#endHeader()
107         */
108        public void endHeader() throws MimeException {
109            expect(Header.class);
110            Header h = (Header) stack.pop();
111            expect(Entity.class);
112            ((Entity) stack.peek()).setHeader(h);
113        }
114        
115        /**
116         * @see org.apache.james.mime4j.parser.ContentHandler#startMultipart(org.apache.james.mime4j.descriptor.BodyDescriptor)
117         */
118        public void startMultipart(final BodyDescriptor bd) throws MimeException {
119            expect(Entity.class);
120            
121            final Entity e = (Entity) stack.peek();
122            final String subType = bd.getSubType();
123            final Multipart multiPart = new Multipart(subType);
124            e.setBody(multiPart);
125            stack.push(multiPart);
126        }
127        
128        /**
129         * @see org.apache.james.mime4j.parser.ContentHandler#body(org.apache.james.mime4j.descriptor.BodyDescriptor, java.io.InputStream)
130         */
131        public void body(BodyDescriptor bd, final InputStream is) throws MimeException, IOException {
132            expect(Entity.class);
133            
134            final String enc = bd.getTransferEncoding();
135            
136            final Body body;
137            
138            final InputStream decodedStream;
139            if (MimeUtil.ENC_BASE64.equals(enc)) {
140                decodedStream = new Base64InputStream(is);
141            } else if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(enc)) {
142                decodedStream = new QuotedPrintableInputStream(is);
143            } else {
144                decodedStream = is;
145            }
146            
147            if (bd.getMimeType().startsWith("text/")) {
148                body = bodyFactory.textBody(decodedStream, bd.getCharset());
149            } else {
150                body = bodyFactory.binaryBody(decodedStream);
151            }
152            
153            Entity entity = ((Entity) stack.peek());
154            entity.setBody(body);
155        }
156        
157        /**
158         * @see org.apache.james.mime4j.parser.ContentHandler#endMultipart()
159         */
160        public void endMultipart() throws MimeException {
161            stack.pop();
162        }
163        
164        /**
165         * @see org.apache.james.mime4j.parser.ContentHandler#startBodyPart()
166         */
167        public void startBodyPart() throws MimeException {
168            expect(Multipart.class);
169            
170            BodyPart bodyPart = new BodyPart();
171            ((Multipart) stack.peek()).addBodyPart(bodyPart);
172            stack.push(bodyPart);
173        }
174        
175        /**
176         * @see org.apache.james.mime4j.parser.ContentHandler#endBodyPart()
177         */
178        public void endBodyPart() throws MimeException {
179            expect(BodyPart.class);
180            stack.pop();
181        }
182        
183        /**
184         * @see org.apache.james.mime4j.parser.ContentHandler#epilogue(java.io.InputStream)
185         */
186        public void epilogue(InputStream is) throws MimeException, IOException {
187            expect(Multipart.class);
188            ByteSequence bytes = loadStream(is);
189            ((Multipart) stack.peek()).setEpilogueRaw(bytes);
190        }
191        
192        /**
193         * @see org.apache.james.mime4j.parser.ContentHandler#preamble(java.io.InputStream)
194         */
195        public void preamble(InputStream is) throws MimeException, IOException {
196            expect(Multipart.class);
197            ByteSequence bytes = loadStream(is);
198            ((Multipart) stack.peek()).setPreambleRaw(bytes);
199        }
200        
201        /**
202         * Unsupported.
203         * @see org.apache.james.mime4j.parser.ContentHandler#raw(java.io.InputStream)
204         */
205        public void raw(InputStream is) throws MimeException, IOException {
206            throw new UnsupportedOperationException("Not supported");
207        }
208    
209        private static ByteSequence loadStream(InputStream in) throws IOException {
210            ByteArrayBuffer bab = new ByteArrayBuffer(64);
211    
212            int b;
213            while ((b = in.read()) != -1) {
214                bab.append(b);
215            }
216    
217            return bab;
218        }
219    
220    }