001: /****************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one *
003: * or more contributor license agreements. See the NOTICE file *
004: * distributed with this work for additional information *
005: * regarding copyright ownership. The ASF licenses this file *
006: * to you under the Apache License, Version 2.0 (the *
007: * "License"); you may not use this file except in compliance *
008: * with the License. You may obtain a copy of the License at *
009: * *
010: * http://www.apache.org/licenses/LICENSE-2.0 *
011: * *
012: * Unless required by applicable law or agreed to in writing, *
013: * software distributed under the License is distributed on an *
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015: * KIND, either express or implied. See the License for the *
016: * specific language governing permissions and limitations *
017: * under the License. *
018: ****************************************************************/package org.apache.james.util;
019:
020: import java.io.FilterOutputStream;
021: import java.io.IOException;
022: import java.io.OutputStream;
023:
024: /**
025: * Adds extra dot if dot occurs in message body at beginning of line (according to RFC1939)
026: * Compare also org.apache.james.smtpserver.SMTPInputStream
027: */
028: public class ExtraDotOutputStream extends FilterOutputStream {
029:
030: /*
031: static public void main(String[] args) throws IOException
032: {
033: String data = ".This is a test\r\nof the thing.\r\nWe should not have much trouble.\r\n.doubled?\r\nor not?\n.doubled\nor not?\r\n\r\n\n\n\r\r\r\n";
034:
035: OutputStream os = new ExtraDotOutputStream(System.out);
036: os.write(data.getBytes());
037: }
038: */
039:
040: /**
041: * Counter for number of last (0A or 0D).
042: */
043: protected int countLast0A0D;
044:
045: /**
046: * Constructor that wraps an OutputStream.
047: *
048: * @param out the OutputStream to be wrapped
049: */
050: public ExtraDotOutputStream(OutputStream out) {
051: super (out);
052: countLast0A0D = 2; // we already assume a CRLF at beginning (otherwise TOP would not work correctly !)
053: }
054:
055: /**
056: * Writes a byte to the stream, adding dots where appropriate.
057: * Also fixes any naked CR or LF to the RFC 2821 mandated CFLF
058: * pairing.
059: *
060: * @param b the byte to write
061: *
062: * @throws IOException if an error occurs writing the byte
063: */
064: public void write(int b) throws IOException {
065: switch (b) {
066: case '.':
067: if (countLast0A0D == 2) {
068: // add extra dot (the first of the pair)
069: out.write('.');
070: }
071: countLast0A0D = 0;
072: break;
073: case '\r':
074: if (countLast0A0D == 1)
075: out.write('\n'); // two CR in a row, so insert an LF first
076: countLast0A0D = 1;
077: break;
078: case '\n':
079: /* RFC 2821 #2.3.7 mandates that line termination is
080: * CRLF, and that CR and LF must not be transmitted
081: * except in that pairing. If we get a naked LF,
082: * convert to CRLF.
083: */
084: if (countLast0A0D != 1)
085: out.write('\r');
086: countLast0A0D = 2;
087: break;
088: default:
089: // we're no longer at the start of a line
090: countLast0A0D = 0;
091: break;
092: }
093: out.write(b);
094: }
095:
096: /**
097: * Ensure that the stream is CRLF terminated.
098: *
099: * @throws IOException if an error occurs writing the byte
100: */
101: public void checkCRLFTerminator() throws IOException {
102: if (countLast0A0D != 2) {
103: write('\n');
104: }
105: }
106: }
|