1 /************************************************************************
2 * Copyright (c) 2000-2006 The Apache Software Foundation. *
3 * All rights reserved. *
4 * ------------------------------------------------------------------- *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you *
6 * may not use this file except in compliance with the License. You *
7 * may obtain a copy of the License at: *
8 * *
9 * http://www.apache.org/licenses/LICENSE-2.0 *
10 * *
11 * Unless required by applicable law or agreed to in writing, software *
12 * distributed under the License is distributed on an "AS IS" BASIS, *
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14 * implied. See the License for the specific language governing *
15 * permissions and limitations under the License. *
16 ***********************************************************************/
17
18 package org.apache.james.util;
19
20 import java.io.InputStream;
21 import java.io.Reader;
22 import java.io.UnsupportedEncodingException;
23 import java.io.IOException;
24
25 /***
26 * A Reader for use with SMTP or other protocols in which lines
27 * must end with CRLF. Extends Reader and overrides its
28 * readLine() method. The Reader readLine() method cannot
29 * serve for SMTP because it ends lines with either CR or LF alone.
30 */
31 public class CRLFTerminatedReader extends Reader {
32
33 public class TerminationException extends IOException {
34 private int where;
35 public TerminationException(int where) {
36 super();
37 this.where = where;
38 }
39
40 public TerminationException(String s, int where) {
41 super(s);
42 this.where = where;
43 }
44
45 public int position() {
46 return where;
47 }
48 }
49
50 public class LineLengthExceededException extends IOException {
51 public LineLengthExceededException(String s) {
52 super(s);
53 }
54 }
55
56 /***
57 * Constructs this CRLFTerminatedReader.
58 * @param in an InputStream
59 * @param charsetName the String name of a supported charset.
60 * "ASCII" is common here.
61 * @throws UnsupportedEncodingException if the named charset
62 * is not supported
63 */
64 InputStream in;
65
66 public CRLFTerminatedReader(InputStream in) {
67 this.in = in;
68 }
69
70 public CRLFTerminatedReader(InputStream in, String enc) throws UnsupportedEncodingException {
71 this(in);
72 }
73
74 private StringBuffer lineBuffer = new StringBuffer();
75 private final int
76 EOF = -1,
77 CR = 13,
78 LF = 10;
79
80 private int tainted = -1;
81
82 /***
83 * Read a line of text which is terminated by CRLF. The concluding
84 * CRLF characters are not returned with the String, but if either CR
85 * or LF appears in the text in any other sequence it is returned
86 * in the String like any other character. Some characters at the
87 * end of the stream may be lost if they are in a "line" not
88 * terminated by CRLF.
89 *
90 * @return either a String containing the contents of a
91 * line which must end with CRLF, or null if the end of the
92 * stream has been reached, possibly discarding some characters
93 * in a line not terminated with CRLF.
94 * @throws IOException if an I/O error occurs.
95 */
96 public String readLine() throws IOException{
97
98
99 lineBuffer.delete(0, lineBuffer.length());
100
101
102
103
104
105 boolean cr_just_received = false;
106
107
108
109
110 while (lineBuffer.length() <= 2048) {
111 int inChar = read();
112
113 if (!cr_just_received){
114
115 switch (inChar){
116 case CR : cr_just_received = true;
117 break;
118 case EOF : return null;
119 case LF :
120 if (tainted == -1) tainted = lineBuffer.length();
121
122 default : lineBuffer.append((char)inChar);
123 }
124 }else{
125
126 switch (inChar){
127 case LF :
128 if (tainted != -1) {
129 int pos = tainted;
130 tainted = -1;
131 throw new TerminationException("\"bare\" CR or LF in data stream", pos);
132 }
133 return lineBuffer.toString();
134 case EOF : return null;
135 case CR :
136 if (tainted == -1) tainted = lineBuffer.length();
137 lineBuffer.append((char)CR);
138 break;
139 default :
140 if (tainted == -1) tainted = lineBuffer.length();
141 lineBuffer.append((char)CR);
142 lineBuffer.append((char)inChar);
143 cr_just_received = false;
144 }
145 }
146 }
147 throw new LineLengthExceededException("Exceeded maximum line length");
148 }
149
150 public int read() throws IOException {
151 return in.read();
152 }
153
154 public boolean ready() throws IOException {
155 return in.available() > 0;
156 }
157
158 public int read(char cbuf[], int off, int len) throws IOException {
159 byte [] temp = new byte[len];
160 int result = in.read(temp, 0, len);
161 for (int i=0;i<result;i++) cbuf[i] = (char) temp[i];
162 return result;
163 }
164
165 public void close() throws IOException {
166 in.close();
167 }
168 }