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 }