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.geronimo.javamail.util;
21  
22  import java.io.IOException;
23  import java.io.Reader; 
24  
25  /**
26   * An implementation of an OutputStream that performs MIME linebreak
27   * canonicalization and "byte-stuff" so that data content does not get mistaken
28   * for a message data-end marker (CRLF.CRLF)l
29   * 
30   * @version $Rev: 597135 $ $Date: 2007-11-21 11:26:57 -0500 (Wed, 21 Nov 2007) $
31   */
32  public class MIMEInputReader extends Reader {
33  
34      // the wrappered output stream.
35      protected Reader source;
36  
37      // a flag to indicate we've just processed a line break. This is used for
38      // byte stuffing purposes. This
39      // is initially true, because if the first character of the content is a
40      // period, we need to byte-stuff
41      // immediately.
42      protected boolean atLineBreak = true;
43      // we've hit the terminating marker on the data
44      protected boolean endOfData = false; 
45      
46  
47      /**
48       * Create an input reader that reads from the source input reader  
49       * 
50       * @param out
51       *            The wrapped Reader        
52       */
53      public MIMEInputReader(Reader source) {
54          this.source = source; 
55      }
56      
57      /**
58       * Concrete implementation of the Reader read() 
59       * abstract method.  This appears to be the only 
60       * abstract method, so all of the other reads must 
61       * funnel through this method. 
62       * 
63       * @param buffer The buffer to fill.
64       * @param off    The offset to start adding characters.
65       * @param len    The number of requested characters.
66       * 
67       * @return The actual count of characters read.  Returns -1 
68       *         if we hit an EOF without reading any characters.
69       * @exception IOException
70       */
71      public int read(char buffer[], int off, int len) throws IOException {
72          // we've been asked for nothing, we'll return nothing. 
73          if (len == 0) {
74              return 0; 
75          }
76          
77          // have we hit the end of data?  Return a -1 indicator
78          if (endOfData) {
79              return -1; 
80          }
81          
82          // number of bytes read 
83          int bytesRead = 0; 
84          
85          int lastRead; 
86          
87          while (bytesRead < len && (lastRead = source.read()) >= 0) {
88              // We are checking for the end of a multiline response
89              // the format is .CRLF
90              
91              // we also have to check for byte-stuffing situation 
92              // where we remove a leading period.  
93              if (atLineBreak && lastRead == '.') {
94                  // step to the next character 
95                  lastRead = source.read();
96                  // we have ".CR"...this is our end of stream 
97                  // marker.  Consume the LF from the reader and return 
98                  if (lastRead == '\r') {
99                      source.read(); 
100                     // no more reads from this point. 
101                     endOfData = true; 
102                     break; 
103                 }
104                 // the next character SHOULD be a ".".  We swallow the first 
105                 // dot and just write the next character to the buffer 
106                 atLineBreak = false; 
107             }
108             else if (lastRead == '\n') {
109                 // hit an end-of-line marker?
110                 // remember we just had a line break 
111                 atLineBreak = true; 
112             }
113             else 
114             {
115                 // something other than a line break character 
116                 atLineBreak = false; 
117             }
118             // add the character to the buffer 
119             buffer[off++] = (char)lastRead; 
120             bytesRead++; 
121         }
122         
123         // we must have had an EOF condition of some sort 
124         if (bytesRead == 0) {
125             return -1; 
126         }
127         // return the actual length read in 
128         return bytesRead; 
129     }
130     
131      /**
132       * Close the stream.  This is a NOP for this stream.  
133       * 
134       * @exception IOException
135       */
136      public void close() throws IOException {
137          // does nothing 
138      }
139 }
140