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.jdkim.canon;
21  
22  import java.io.FilterOutputStream;
23  import java.io.IOException;
24  import java.io.OutputStream;
25  
26  /**
27   * Implements Simple canonicalization for the body as defined in RFC4871 -
28   * 3.4.3. The "simple" Body Canonicalization Algorithm
29   */
30  public class SimpleBodyCanonicalizer extends FilterOutputStream {
31  
32      private static final boolean DEEP_DEBUG = false;
33  
34      private boolean lastWasCR;
35      private int countCRLF;
36  
37      public SimpleBodyCanonicalizer(OutputStream arg0) {
38          super(arg0);
39      }
40  
41      public void write(byte[] b, int off, int len) throws IOException {
42          if (len <= 0)
43              return;
44          if (DEEP_DEBUG)
45              System.out.println("I:(" + lastWasCR + "|" + countCRLF + ") ["
46                      + new String(b, off, len) + "]");
47          if (lastWasCR) {
48              if (len > 0 && b[off] == '\n') {
49                  countCRLF++;
50                  lastWasCR = false;
51                  off++;
52                  len--;
53              } else {
54                  // TODO output the lone \r ? (this condition should never happen
55                  // as we expect only CRLF in a compliant 7bit email.
56                  out.write('\r');
57                  lastWasCR = false;
58              }
59          }
60          int newCountCRLF = 0;
61          boolean newLastWasCR = false;
62          if (len >= 1 && b[off + len - 1] == '\r') {
63              newLastWasCR = true;
64              len--;
65          }
66          while (len >= 2 && b[off + len - 1] == '\n' && b[off + len - 2] == '\r') {
67              len -= 2;
68              newCountCRLF++;
69          }
70          if (len > 0) {
71              dumpCRLF();
72              out.write(b, off, len);
73          }
74          countCRLF += newCountCRLF;
75          lastWasCR = newLastWasCR;
76      }
77  
78      public void write(int b) throws IOException {
79          if (DEEP_DEBUG)
80              System.out.println("B:(" + lastWasCR + "|" + countCRLF + ") ["
81                      + new String("" + (char) b) + "]");
82          if (lastWasCR && '\n' == b) {
83              lastWasCR = false;
84              countCRLF++;
85          } else {
86              if (!lastWasCR && '\r' == b) {
87                  lastWasCR = true;
88              } else {
89                  dumpCRLF();
90                  if ('\r' == b)
91                      lastWasCR = true;
92                  else
93                      out.write(b);
94              }
95          }
96      }
97  
98      public void close() throws IOException {
99          complete();
100         super.close();
101     }
102 
103     private void complete() throws IOException {
104         if (DEEP_DEBUG)
105             System.out.println("C:(" + lastWasCR + "|" + countCRLF + ")");
106         if (lastWasCR) {
107             // if the last char was a CR we'll let dumpCRLF
108             // to output the missing \n
109             lastWasCR = false;
110         }
111         countCRLF = 1;
112         dumpCRLF();
113     }
114 
115     private void dumpCRLF() throws IOException {
116         if (DEEP_DEBUG)
117             System.out.println("D:(" + lastWasCR + "|" + countCRLF + ")");
118         if (lastWasCR) {
119             out.write('\r');
120             lastWasCR = false;
121         }
122         while (countCRLF > 0) {
123             out.write("\r\n".getBytes());
124             countCRLF--;
125         }
126     }
127 
128 }