Home | History | Annotate | Download | only in message
      1 /****************************************************************
      2  * Licensed to the Apache Software Foundation (ASF) under one   *
      3  * or more contributor license agreements.  See the NOTICE file *
      4  * distributed with this work for additional information        *
      5  * regarding copyright ownership.  The ASF licenses this file   *
      6  * to you under the Apache License, Version 2.0 (the            *
      7  * "License"); you may not use this file except in compliance   *
      8  * with the License.  You may obtain a copy of the License at   *
      9  *                                                              *
     10  *   http://www.apache.org/licenses/LICENSE-2.0                 *
     11  *                                                              *
     12  * Unless required by applicable law or agreed to in writing,   *
     13  * software distributed under the License is distributed on an  *
     14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
     15  * KIND, either express or implied.  See the License for the    *
     16  * specific language governing permissions and limitations      *
     17  * under the License.                                           *
     18  ****************************************************************/
     19 
     20 package org.apache.james.mime4j.message;
     21 
     22 import java.io.IOException;
     23 import java.io.InputStream;
     24 import java.io.OutputStream;
     25 import java.util.Stack;
     26 
     27 import org.apache.james.mime4j.BodyDescriptor;
     28 import org.apache.james.mime4j.ContentHandler;
     29 import org.apache.james.mime4j.MimeStreamParser;
     30 import org.apache.james.mime4j.decoder.Base64InputStream;
     31 import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
     32 import org.apache.james.mime4j.field.Field;
     33 import org.apache.james.mime4j.field.UnstructuredField;
     34 
     35 
     36 /**
     37  * Represents a MIME message. The following code parses a stream into a
     38  * <code>Message</code> object.
     39  *
     40  * <pre>
     41  *      Message msg = new Message(new BufferedInputStream(
     42  *                                      new FileInputStream("mime.msg")));
     43  * </pre>
     44  *
     45  *
     46  *
     47  * @version $Id: Message.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
     48  */
     49 public class Message extends Entity implements Body {
     50 
     51     /**
     52      * Creates a new empty <code>Message</code>.
     53      */
     54     public Message() {
     55     }
     56 
     57     /**
     58      * Parses the specified MIME message stream into a <code>Message</code>
     59      * instance.
     60      *
     61      * @param is the stream to parse.
     62      * @throws IOException on I/O errors.
     63      */
     64     public Message(InputStream is) throws IOException {
     65         MimeStreamParser parser = new MimeStreamParser();
     66         parser.setContentHandler(new MessageBuilder());
     67         parser.parse(is);
     68     }
     69 
     70 
     71     /**
     72      * Gets the <code>Subject</code> field.
     73      *
     74      * @return the <code>Subject</code> field or <code>null</code> if it
     75      *         doesn't exist.
     76      */
     77     public UnstructuredField getSubject() {
     78         return (UnstructuredField) getHeader().getField(Field.SUBJECT);
     79     }
     80 
     81     /**
     82      *
     83      * @see org.apache.james.mime4j.message.Entity#writeTo(java.io.OutputStream)
     84      */
     85     @Override
     86     public void writeTo(OutputStream out) throws IOException {
     87         getHeader().writeTo(out);
     88 
     89         Body body = getBody();
     90         if (body instanceof Multipart) {
     91             Multipart mp = (Multipart) body;
     92             mp.writeTo(out);
     93         } else {
     94             body.writeTo(out);
     95         }
     96     }
     97 
     98 
     99     private class MessageBuilder implements ContentHandler {
    100         private Stack<Object> stack = new Stack<Object>();
    101 
    102         public MessageBuilder() {
    103         }
    104 
    105         private void expect(Class c) {
    106             if (!c.isInstance(stack.peek())) {
    107                 throw new IllegalStateException("Internal stack error: "
    108                         + "Expected '" + c.getName() + "' found '"
    109                         + stack.peek().getClass().getName() + "'");
    110             }
    111         }
    112 
    113         /**
    114          * @see org.apache.james.mime4j.ContentHandler#startMessage()
    115          */
    116         public void startMessage() {
    117             if (stack.isEmpty()) {
    118                 stack.push(Message.this);
    119             } else {
    120                 expect(Entity.class);
    121                 Message m = new Message();
    122                 ((Entity) stack.peek()).setBody(m);
    123                 stack.push(m);
    124             }
    125         }
    126 
    127         /**
    128          * @see org.apache.james.mime4j.ContentHandler#endMessage()
    129          */
    130         public void endMessage() {
    131             expect(Message.class);
    132             stack.pop();
    133         }
    134 
    135         /**
    136          * @see org.apache.james.mime4j.ContentHandler#startHeader()
    137          */
    138         public void startHeader() {
    139             stack.push(new Header());
    140         }
    141 
    142         /**
    143          * @see org.apache.james.mime4j.ContentHandler#field(java.lang.String)
    144          */
    145         public void field(String fieldData) {
    146             expect(Header.class);
    147             ((Header) stack.peek()).addField(Field.parse(fieldData));
    148         }
    149 
    150         /**
    151          * @see org.apache.james.mime4j.ContentHandler#endHeader()
    152          */
    153         public void endHeader() {
    154             expect(Header.class);
    155             Header h = (Header) stack.pop();
    156             expect(Entity.class);
    157             ((Entity) stack.peek()).setHeader(h);
    158         }
    159 
    160         /**
    161          * @see org.apache.james.mime4j.ContentHandler#startMultipart(org.apache.james.mime4j.BodyDescriptor)
    162          */
    163         public void startMultipart(BodyDescriptor bd) {
    164             expect(Entity.class);
    165 
    166             Entity e = (Entity) stack.peek();
    167             Multipart multiPart = new Multipart();
    168             e.setBody(multiPart);
    169             stack.push(multiPart);
    170         }
    171 
    172         /**
    173          * @see org.apache.james.mime4j.ContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)
    174          */
    175         public void body(BodyDescriptor bd, InputStream is) throws IOException {
    176             expect(Entity.class);
    177 
    178             String enc = bd.getTransferEncoding();
    179             if ("base64".equals(enc)) {
    180                 is = new Base64InputStream(is);
    181             } else if ("quoted-printable".equals(enc)) {
    182                 is = new QuotedPrintableInputStream(is);
    183             }
    184 
    185             Body body = null;
    186             if (bd.getMimeType().startsWith("text/")) {
    187                 body = new MemoryTextBody(is, bd.getCharset());
    188             } else {
    189                 body = new MemoryBinaryBody(is);
    190             }
    191 
    192             ((Entity) stack.peek()).setBody(body);
    193         }
    194 
    195         /**
    196          * @see org.apache.james.mime4j.ContentHandler#endMultipart()
    197          */
    198         public void endMultipart() {
    199             stack.pop();
    200         }
    201 
    202         /**
    203          * @see org.apache.james.mime4j.ContentHandler#startBodyPart()
    204          */
    205         public void startBodyPart() {
    206             expect(Multipart.class);
    207 
    208             BodyPart bodyPart = new BodyPart();
    209             ((Multipart) stack.peek()).addBodyPart(bodyPart);
    210             stack.push(bodyPart);
    211         }
    212 
    213         /**
    214          * @see org.apache.james.mime4j.ContentHandler#endBodyPart()
    215          */
    216         public void endBodyPart() {
    217             expect(BodyPart.class);
    218             stack.pop();
    219         }
    220 
    221         /**
    222          * @see org.apache.james.mime4j.ContentHandler#epilogue(java.io.InputStream)
    223          */
    224         public void epilogue(InputStream is) throws IOException {
    225             expect(Multipart.class);
    226             StringBuffer sb = new StringBuffer();
    227             int b;
    228             while ((b = is.read()) != -1) {
    229                 sb.append((char) b);
    230             }
    231             ((Multipart) stack.peek()).setEpilogue(sb.toString());
    232         }
    233 
    234         /**
    235          * @see org.apache.james.mime4j.ContentHandler#preamble(java.io.InputStream)
    236          */
    237         public void preamble(InputStream is) throws IOException {
    238             expect(Multipart.class);
    239             StringBuffer sb = new StringBuffer();
    240             int b;
    241             while ((b = is.read()) != -1) {
    242                 sb.append((char) b);
    243             }
    244             ((Multipart) stack.peek()).setPreamble(sb.toString());
    245         }
    246 
    247         /**
    248          * TODO: Implement me
    249          *
    250          * @see org.apache.james.mime4j.ContentHandler#raw(java.io.InputStream)
    251          */
    252         public void raw(InputStream is) throws IOException {
    253             throw new UnsupportedOperationException("Not supported");
    254         }
    255 
    256     }
    257 }
    258