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 */ 019 020 package org.apache.geronimo.javamail.util; 021 022 import java.io.IOException; 023 import java.io.Reader; 024 025 /** 026 * An implementation of an OutputStream that performs MIME linebreak 027 * canonicalization and "byte-stuff" so that data content does not get mistaken 028 * for a message data-end marker (CRLF.CRLF)l 029 * 030 * @version $Rev: 597135 $ $Date: 2007-11-21 11:26:57 -0500 (Wed, 21 Nov 2007) $ 031 */ 032 public class MIMEInputReader extends Reader { 033 034 // the wrappered output stream. 035 protected Reader source; 036 037 // a flag to indicate we've just processed a line break. This is used for 038 // byte stuffing purposes. This 039 // is initially true, because if the first character of the content is a 040 // period, we need to byte-stuff 041 // immediately. 042 protected boolean atLineBreak = true; 043 // we've hit the terminating marker on the data 044 protected boolean endOfData = false; 045 046 047 /** 048 * Create an input reader that reads from the source input reader 049 * 050 * @param out 051 * The wrapped Reader 052 */ 053 public MIMEInputReader(Reader source) { 054 this.source = source; 055 } 056 057 /** 058 * Concrete implementation of the Reader read() 059 * abstract method. This appears to be the only 060 * abstract method, so all of the other reads must 061 * funnel through this method. 062 * 063 * @param buffer The buffer to fill. 064 * @param off The offset to start adding characters. 065 * @param len The number of requested characters. 066 * 067 * @return The actual count of characters read. Returns -1 068 * if we hit an EOF without reading any characters. 069 * @exception IOException 070 */ 071 public int read(char buffer[], int off, int len) throws IOException { 072 // we've been asked for nothing, we'll return nothing. 073 if (len == 0) { 074 return 0; 075 } 076 077 // have we hit the end of data? Return a -1 indicator 078 if (endOfData) { 079 return -1; 080 } 081 082 // number of bytes read 083 int bytesRead = 0; 084 085 int lastRead; 086 087 while (bytesRead < len && (lastRead = source.read()) >= 0) { 088 // We are checking for the end of a multiline response 089 // the format is .CRLF 090 091 // we also have to check for byte-stuffing situation 092 // where we remove a leading period. 093 if (atLineBreak && lastRead == '.') { 094 // step to the next character 095 lastRead = source.read(); 096 // we have ".CR"...this is our end of stream 097 // marker. Consume the LF from the reader and return 098 if (lastRead == '\r') { 099 source.read(); 100 // no more reads from this point. 101 endOfData = true; 102 break; 103 } 104 // the next character SHOULD be a ".". We swallow the first 105 // dot and just write the next character to the buffer 106 atLineBreak = false; 107 } 108 else if (lastRead == '\n') { 109 // hit an end-of-line marker? 110 // remember we just had a line break 111 atLineBreak = true; 112 } 113 else 114 { 115 // something other than a line break character 116 atLineBreak = false; 117 } 118 // add the character to the buffer 119 buffer[off++] = (char)lastRead; 120 bytesRead++; 121 } 122 123 // we must have had an EOF condition of some sort 124 if (bytesRead == 0) { 125 return -1; 126 } 127 // return the actual length read in 128 return bytesRead; 129 } 130 131 /** 132 * Close the stream. This is a NOP for this stream. 133 * 134 * @exception IOException 135 */ 136 public void close() throws IOException { 137 // does nothing 138 } 139 } 140