/* Headers822.java * * Copyright (c) 2002, Benja Fallenstein * * You may use and distribute under the terms of either the GNU Lesser * General Public License, either version 2 of the license or, * at your choice, any later version. Alternatively, you may use and * distribute under the terms of the XPL. * * See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of * the licenses. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the README * file for more details. * */ /* * Written by Benja Fallenstein */ package gzz.storm.headers; import java.io.*; import java.util.*; /** Utility functions for reading and writing RFC 2822-style headers. * The parsing functions in this util are strict in that they don't * try to handle non-conformant headers (e.g., headers that use * only CR or LF instead of CRLF). They also aren't fully conformant * because they do not currently attempt to parse obsoleted syntax, * as required by RFC 2822; support for this may be added * in the future. *
* So far, there is no support at all for structured field values.
*/
public class Headers822 {
public static boolean dbg = false;
private static void p(String s) { System.out.println(s); }
/** Read and parse a header.
* After this method, in
is positioned
* at the beginning of the message body, after the empty line
* ending the header.
*/
public static Header822 readHeader(InputStream in) throws IOException {
List lines = new ArrayList();
HeaderMap822 map = new DefaultHeaderMap822();
String fieldName = null;
String fieldBody = null;
while(true) {
String line = readLine(in);
if(line.equals("")) {
// add the previous line before returning
if(dbg) p("add last: "+fieldName+", "+fieldBody);
if(fieldName != null)
map.add(fieldName, fieldBody.trim());
HeaderLines822 hlines = new DefaultHeaderLines822(lines);
return new ModularHeader822(hlines, map);
}
lines.add(line);
if(line.trim().equals(""))
throw new ParseException("Line containing only whitespace " +
"in headers");
if(!Character.isSpace(line.charAt(0))) {
// not a continuation of a folded line
// first, add the previous line
if(dbg) p("add: "+fieldName+", "+fieldBody);
if(fieldName != null)
map.add(fieldName, fieldBody.trim());
// then, remember name and body of the current line
int i = line.indexOf(':');
if(i <= 0)
throw new ParseException("No header name in line '" +
line + "'");
fieldName = line.substring(0, i).toLowerCase();
fieldBody = line.substring(i+1);
} else if(fieldName == null) {
throw new ParseException("First line in header is folded");
} else {
fieldBody += line;
}
}
}
public static String canonicalizeFieldName(String fieldName) {
if(fieldName.equals(""))
throw new IllegalArgumentException("Empty header field name");
String s = fieldName.toLowerCase();
int i = 0;
do {
s = s.substring(0, i) + s.substring(i,i+1).toUpperCase() +
s.substring(i+1);
i = s.indexOf('-', i+1)+1;
} while(i > 0);
if(dbg) p("Canonicalized header field name: "+s);
return s;
}
// ParseException
public static class ParseException extends IOException {
public ParseException(String s) { super(s); }
}
// Private stuff
private static int CR = 0xd, LF = 0xa;
private static String readLine(InputStream in) throws IOException {
StringBuffer s = new StringBuffer();
while(true) {
int c = in.read();
if(c < 0)
throw new ParseException("EOF while reading headers");
if(c == CR) break;
if(c == LF)
throw new ParseException("LF without preceding CR");
if(c < 1 || c > 127)
throw new ParseException("Character outside US-ASCII " +
"1-127: '"+c+"'");
s.append((char)c);
}
int i = in.read();
if(i < 0)
throw new ParseException("EOF while reading headers");
if(i != LF)
throw new ParseException("CR not followed by LF");
return s.toString();
}
}