View Javadoc

1   /**
2    *
3    * Copyright 2006 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package org.apache.geronimo.javamail.util;
19  
20  import java.io.IOException;
21  import java.io.OutputStream;
22  
23  /**
24   * An implementation of an OutputStream that performs MIME linebreak
25   * canonicalization and "byte-stuff" so that data content does not get mistaken
26   * for a message data-end marker (CRLF.CRLF)l
27   * 
28   * @version $Rev: 432884 $ $Date: 2006-08-19 14:53:20 -0700 (Sat, 19 Aug 2006) $
29   */
30  public class MIMEOutputStream extends OutputStream {
31  
32      // the wrappered output stream.
33      protected OutputStream out;
34  
35      // last character we handled...used to recongnize line breaks.
36      protected int lastWrite = -1;
37  
38      // a flag to indicate we've just processed a line break. This is used for
39      // byte stuffing purposes. This
40      // is initially true, because if the first character of the content is a
41      // period, we need to byte-stuff
42      // immediately.
43      protected boolean atLineBreak = true;
44  
45      /**
46       * Create an output stream that writes to the target output stream.
47       * 
48       * @param out
49       *            The wrapped output stream.
50       */
51      public MIMEOutputStream(OutputStream out) {
52          this.out = out;
53      }
54  
55      // in order for this to work, we only need override the single character
56      // form, as the others
57      // funnel through this one by default.
58      public void write(int ch) throws IOException {
59          // if this is a CR character, always write out a full sequence, and
60          // remember that we just did this.
61          if (ch == '\r') {
62              out.write((byte) '\r');
63              out.write((byte) '\n');
64              // we've just taken a break;
65              atLineBreak = true;
66          }
67          // if this is a new line, then we need to determine if this is a loner
68          // or part of a CRLF sequence.
69          else if (ch == '\n') {
70              // is this a lone ranger?
71              if (lastWrite != '\r') {
72                  // write the full CRLF sequence.
73                  out.write((byte) '\r');
74                  out.write((byte) '\n');
75              }
76              // regardless of whether we wrote something or not, we're still at a
77              // line break.
78              atLineBreak = true;
79          }
80          // potential byte-stuffing situation?
81          else if (ch == '.') {
82              // ok, this is a potential stuff situation. Did we just have a line
83              // break? Double up the character.
84              if (atLineBreak) {
85                  out.write('.');
86              }
87              out.write('.');
88              atLineBreak = false;
89          } else {
90              // just write this out and flip the linebreak flag.
91              out.write(ch);
92              atLineBreak = false;
93          }
94          // remember this last one for CRLF tracking purposes.
95          lastWrite = ch;
96      }
97  }