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.geronimo.javamail.util; 21 22 import java.io.IOException; 23 import java.io.Reader; 24 25 /** 26 * An implementation of an OutputStream that performs MIME linebreak 27 * canonicalization and "byte-stuff" so that data content does not get mistaken 28 * for a message data-end marker (CRLF.CRLF)l 29 * 30 * @version $Rev: 597135 $ $Date: 2007-11-21 11:26:57 -0500 (Wed, 21 Nov 2007) $ 31 */ 32 public class MIMEInputReader extends Reader { 33 34 // the wrappered output stream. 35 protected Reader source; 36 37 // a flag to indicate we've just processed a line break. This is used for 38 // byte stuffing purposes. This 39 // is initially true, because if the first character of the content is a 40 // period, we need to byte-stuff 41 // immediately. 42 protected boolean atLineBreak = true; 43 // we've hit the terminating marker on the data 44 protected boolean endOfData = false; 45 46 47 /** 48 * Create an input reader that reads from the source input reader 49 * 50 * @param out 51 * The wrapped Reader 52 */ 53 public MIMEInputReader(Reader source) { 54 this.source = source; 55 } 56 57 /** 58 * Concrete implementation of the Reader read() 59 * abstract method. This appears to be the only 60 * abstract method, so all of the other reads must 61 * funnel through this method. 62 * 63 * @param buffer The buffer to fill. 64 * @param off The offset to start adding characters. 65 * @param len The number of requested characters. 66 * 67 * @return The actual count of characters read. Returns -1 68 * if we hit an EOF without reading any characters. 69 * @exception IOException 70 */ 71 public int read(char buffer[], int off, int len) throws IOException { 72 // we've been asked for nothing, we'll return nothing. 73 if (len == 0) { 74 return 0; 75 } 76 77 // have we hit the end of data? Return a -1 indicator 78 if (endOfData) { 79 return -1; 80 } 81 82 // number of bytes read 83 int bytesRead = 0; 84 85 int lastRead; 86 87 while (bytesRead < len && (lastRead = source.read()) >= 0) { 88 // We are checking for the end of a multiline response 89 // the format is .CRLF 90 91 // we also have to check for byte-stuffing situation 92 // where we remove a leading period. 93 if (atLineBreak && lastRead == '.') { 94 // step to the next character 95 lastRead = source.read(); 96 // we have ".CR"...this is our end of stream 97 // marker. Consume the LF from the reader and return 98 if (lastRead == '\r') { 99 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