View Javadoc

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.parser;
21  
22  import org.apache.james.mime4j.MimeException;
23  import org.apache.james.mime4j.descriptor.BodyDescriptor;
24  
25  import java.io.IOException;
26  import java.io.InputStream;
27  
28  /**
29   * <p>
30   * Parses MIME (or RFC822) message streams of bytes or characters and reports 
31   * parsing events to a <code>ContentHandler</code> instance.
32   * </p>
33   * <p>
34   * Typical usage:<br/>
35   * <pre>
36   *      ContentHandler handler = new MyHandler();
37   *      MimeStreamParser parser = new MimeStreamParser();
38   *      parser.setContentHandler(handler);
39   *      parser.parse(new FileInputStream("mime.msg"));
40   * </pre>
41   */
42  public class MimeStreamParser {
43  
44      private ContentHandler handler = null;
45      private boolean contentDecoding;
46      
47      private final MimeTokenStream mimeTokenStream;
48  
49      public MimeStreamParser(final MimeEntityConfig config) {
50          super();
51          MimeEntityConfig localConfig;
52          if (config != null) {
53              localConfig = config.clone();
54          } else {
55              localConfig = new MimeEntityConfig();
56          }
57          this.mimeTokenStream = new MimeTokenStream(localConfig);
58          this.contentDecoding = false;
59      }
60      
61      public MimeStreamParser() {
62          this(null);
63      }
64      
65      /**
66       * Determines whether this parser automatically decodes body content
67       * based on the on the MIME fields with the standard defaults.
68       */ 
69      public boolean isContentDecoding() {
70          return contentDecoding;
71      }
72  
73      /**
74       * Defines whether parser should automatically decode body content
75       * based on the on the MIME fields with the standard defaults.
76       */ 
77      public void setContentDecoding(boolean b) {
78          this.contentDecoding = b;
79      }
80  
81      /**
82       * Parses a stream of bytes containing a MIME message.
83       * 
84       * @param is the stream to parse.
85       * @throws MimeException if the message can not be processed
86       * @throws IOException on I/O errors.
87       */
88      public void parse(InputStream is) throws MimeException, IOException {
89          mimeTokenStream.parse(is);
90          OUTER: for (;;) {
91              int state = mimeTokenStream.getState();
92              switch (state) {
93                  case MimeTokenStream.T_BODY:
94                      BodyDescriptor desc = mimeTokenStream.getBodyDescriptor();
95                      InputStream bodyContent;
96                      if (contentDecoding) {
97                          bodyContent = mimeTokenStream.getDecodedInputStream(); 
98                      } else {
99                          bodyContent = mimeTokenStream.getInputStream(); 
100                     }
101                     handler.body(desc, bodyContent);
102                     break;
103                 case MimeTokenStream.T_END_BODYPART:
104                     handler.endBodyPart();
105                     break;
106                 case MimeTokenStream.T_END_HEADER:
107                     handler.endHeader();
108                     break;
109                 case MimeTokenStream.T_END_MESSAGE:
110                     handler.endMessage();
111                     break;
112                 case MimeTokenStream.T_END_MULTIPART:
113                     handler.endMultipart();
114                     break;
115                 case MimeTokenStream.T_END_OF_STREAM:
116                     break OUTER;
117                 case MimeTokenStream.T_EPILOGUE:
118                     handler.epilogue(mimeTokenStream.getInputStream());
119                     break;
120                 case MimeTokenStream.T_FIELD:
121                     handler.field(mimeTokenStream.getField());
122                     break;
123                 case MimeTokenStream.T_PREAMBLE:
124                     handler.preamble(mimeTokenStream.getInputStream());
125                     break;
126                 case MimeTokenStream.T_RAW_ENTITY:
127                     handler.raw(mimeTokenStream.getInputStream());
128                     break;
129                 case MimeTokenStream.T_START_BODYPART:
130                     handler.startBodyPart();
131                     break;
132                 case MimeTokenStream.T_START_HEADER:
133                     handler.startHeader();
134                     break;
135                 case MimeTokenStream.T_START_MESSAGE:
136                     handler.startMessage();
137                     break;
138                 case MimeTokenStream.T_START_MULTIPART:
139                     handler.startMultipart(mimeTokenStream.getBodyDescriptor());
140                     break;
141                 default:
142                     throw new IllegalStateException("Invalid state: " + state);
143             }
144             state = mimeTokenStream.next();
145         }
146     }
147     
148     /**
149      * Determines if this parser is currently in raw mode.
150      * 
151      * @return <code>true</code> if in raw mode, <code>false</code>
152      *         otherwise.
153      * @see #setRaw(boolean)
154      */
155     public boolean isRaw() {
156         return mimeTokenStream.isRaw();
157     }
158     
159     /**
160      * Enables or disables raw mode. In raw mode all future entities 
161      * (messages or body parts) in the stream will be reported to the
162      * {@link ContentHandler#raw(InputStream)} handler method only.
163      * The stream will contain the entire unparsed entity contents 
164      * including header fields and whatever is in the body.
165      * 
166      * @param raw <code>true</code> enables raw mode, <code>false</code>
167      *        disables it.
168      */
169     public void setRaw(boolean raw) {
170         mimeTokenStream.setRecursionMode(MimeTokenStream.M_RAW);
171     }
172     
173     /**
174      * Finishes the parsing and stops reading lines.
175      * NOTE: No more lines will be parsed but the parser
176      * will still call 
177      * {@link ContentHandler#endMultipart()},
178      * {@link ContentHandler#endBodyPart()},
179      * {@link ContentHandler#endMessage()}, etc to match previous calls
180      * to 
181      * {@link ContentHandler#startMultipart(BodyDescriptor)},
182      * {@link ContentHandler#startBodyPart()},
183      * {@link ContentHandler#startMessage()}, etc.
184      */
185     public void stop() {
186         mimeTokenStream.stop();
187     }
188     
189     /**
190      * Sets the <code>ContentHandler</code> to use when reporting 
191      * parsing events.
192      * 
193      * @param h the <code>ContentHandler</code>.
194      */
195     public void setContentHandler(ContentHandler h) {
196         this.handler = h;
197     }
198 
199 }