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     public void writeTo(OutputStream out) throws IOException {
     86         getHeader().writeTo(out);
     87 
     88         Body body = getBody();
     89         if (body instanceof Multipart) {
     90             Multipart mp = (Multipart) body;
     91             mp.writeTo(out);
     92         } else {
     93             body.writeTo(out);
     94         }
     95     }
     96 
     97 
     98     private class MessageBuilder implements ContentHandler {
     99         private Stack stack = new Stack();
    100 
    101         public MessageBuilder() {
    102         }
    103 
    104         private void expect(Class c) {
    105             if (!c.isInstance(stack.peek())) {
    106                 throw new IllegalStateException("Internal stack error: "
    107                         + "Expected '" + c.getName() + "' found '"
    108                         + stack.peek().getClass().getName() + "'");
    109             }
    110         }
    111 
    112         /**
    113          * @see org.apache.james.mime4j.ContentHandler#startMessage()
    114          */
    115         public void startMessage() {
    116             if (stack.isEmpty()) {
    117                 stack.push(Message.this);
    118             } else {
    119                 expect(Entity.class);
    120                 Message m = new Message();
    121                 ((Entity) stack.peek()).setBody(m);
    122                 stack.push(m);
    123             }
    124         }
    125 
    126         /**
    127          * @see org.apache.james.mime4j.ContentHandler#endMessage()
    128          */
    129         public void endMessage() {
    130             expect(Message.class);
    131             stack.pop();
    132         }
    133 
    134         /**
    135          * @see org.apache.james.mime4j.ContentHandler#startHeader()
    136          */
    137         public void startHeader() {
    138             stack.push(new Header());
    139         }
    140 
    141         /**
    142          * @see org.apache.james.mime4j.ContentHandler#field(java.lang.String)
    143          */
    144         public void field(String fieldData) {
    145             expect(Header.class);
    146             ((Header) stack.peek()).addField(Field.parse(fieldData));
    147         }
    148 
    149         /**
    150          * @see org.apache.james.mime4j.ContentHandler#endHeader()
    151          */
    152         public void endHeader() {
    153             expect(Header.class);
    154             Header h = (Header) stack.pop();
    155             expect(Entity.class);
    156             ((Entity) stack.peek()).setHeader(h);
    157         }
    158 
    159         /**
    160          * @see org.apache.james.mime4j.ContentHandler#startMultipart(org.apache.james.mime4j.BodyDescriptor)
    161          */
    162         public void startMultipart(BodyDescriptor bd) {
    163             expect(Entity.class);
    164 
    165             Entity e = (Entity) stack.peek();
    166             Multipart multiPart = new Multipart();
    167             e.setBody(multiPart);
    168             stack.push(multiPart);
    169         }
    170 
    171         /**
    172          * @see org.apache.james.mime4j.ContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)
    173          */
    174         public void body(BodyDescriptor bd, InputStream is) throws IOException {
    175             expect(Entity.class);
    176 
    177             String enc = bd.getTransferEncoding();
    178             if ("base64".equals(enc)) {
    179                 is = new Base64InputStream(is);
    180             } else if ("quoted-printable".equals(enc)) {
    181                 is = new QuotedPrintableInputStream(is);
    182             }
    183 
    184             Body body = null;
    185             if (bd.getMimeType().startsWith("text/")) {
    186                 body = new MemoryTextBody(is, bd.getCharset());
    187             } else {
    188                 body = new MemoryBinaryBody(is);
    189             }
    190 
    191             ((Entity) stack.peek()).setBody(body);
    192         }
    193 
    194         /**
    195          * @see org.apache.james.mime4j.ContentHandler#endMultipart()
    196          */
    197         public void endMultipart() {
    198             stack.pop();
    199         }
    200 
    201         /**
    202          * @see org.apache.james.mime4j.ContentHandler#startBodyPart()
    203          */
    204         public void startBodyPart() {
    205             expect(Multipart.class);
    206 
    207             BodyPart bodyPart = new BodyPart();
    208             ((Multipart) stack.peek()).addBodyPart(bodyPart);
    209             stack.push(bodyPart);
    210         }
    211 
    212         /**
    213          * @see org.apache.james.mime4j.ContentHandler#endBodyPart()
    214          */
    215         public void endBodyPart() {
    216             expect(BodyPart.class);
    217             stack.pop();
    218         }
    219 
    220         /**
    221          * @see org.apache.james.mime4j.ContentHandler#epilogue(java.io.InputStream)
    222          */
    223         public void epilogue(InputStream is) throws IOException {
    224             expect(Multipart.class);
    225             StringBuffer sb = new StringBuffer();
    226             int b;
    227             while ((b = is.read()) != -1) {
    228                 sb.append((char) b);
    229             }
    230             ((Multipart) stack.peek()).setEpilogue(sb.toString());
    231         }
    232 
    233         /**
    234          * @see org.apache.james.mime4j.ContentHandler#preamble(java.io.InputStream)
    235          */
    236         public void preamble(InputStream is) throws IOException {
    237             expect(Multipart.class);
    238             StringBuffer sb = new StringBuffer();
    239             int b;
    240             while ((b = is.read()) != -1) {
    241                 sb.append((char) b);
    242             }
    243             ((Multipart) stack.peek()).setPreamble(sb.toString());
    244         }
    245 
    246         /**
    247          * TODO: Implement me
    248          *
    249          * @see org.apache.james.mime4j.ContentHandler#raw(java.io.InputStream)
    250          */
    251         public void raw(InputStream is) throws IOException {
    252             throw new UnsupportedOperationException("Not supported");
    253         }
    254 
    255     }
    256 }
    257