/** * Copyright (c) 2011-2013 Optimax Software Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Optimax Software, ElasticInbox, nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.elasticinbox.common.utils; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; /** * A Filter for use with POP3 or other protocols in which lines must end with * CRLF. Converts every "isolated" occurrence of \r or \n with \r\n. * * @author Rustam Aliev */ public class CRLFInputStream extends FilterInputStream { /** * Last char indicator (CR, LF, or OTHER). */ private int statusLast; private final static int LAST_WAS_OTHER = 0; private final static int LAST_WAS_CR = 1; private final static int LAST_WAS_LF = 2; private static final int CR = 13; private static final int LF = 10; private static final int EMPTY_BUFFER = -1; private static int buffer = EMPTY_BUFFER; public CRLFInputStream(InputStream in) { super(in); statusLast = LAST_WAS_LF; // we already assume a CRLF at beginning } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read() throws IOException { // if buffer not empty, return buffer first if (buffer > EMPTY_BUFFER) { int b = buffer; buffer = EMPTY_BUFFER; return b; } // read byte from stream int b = super.read(); switch (b) { case CR: statusLast = LAST_WAS_CR; break; case LF: if (statusLast != LAST_WAS_CR) { // return CR instead b = CR; statusLast = LAST_WAS_CR; } else { statusLast = LAST_WAS_LF; } break; default: if (statusLast == LAST_WAS_CR) { // add current byte to buffer and return LF buffer = b; b = LF; statusLast = LAST_WAS_LF; } else { // we're no longer at the start of a line statusLast = LAST_WAS_OTHER; } break; } return b; } @Override public int read(byte[] b, int off, int len) throws IOException { // TODO: should be implemented // we don't need it right now and it's a bit complicated return -1; } }