1 /* 2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.org.jvnet.mimepull; 27 28 import java.io.IOException; 29 import java.util.NoSuchElementException; 30 import java.util.List; 31 32 /** 33 * InternetHeaders is a utility class that manages RFC822 style 34 * headers. Given an RFC822 format message stream, it reads lines 35 * until the blank line that indicates end of header. The input stream 36 * is positioned at the start of the body. The lines are stored 37 * within the object and can be extracted as either Strings or 38 * {@link Header} objects. <p> 39 * <p/> 40 * This class is mostly intended for service providers. MimeMessage 41 * and MimeBody use this class for holding their headers. <p> 42 * <p/> 43 * <hr> <strong>A note on RFC822 and MIME headers</strong><p> 44 * <p/> 45 * RFC822 and MIME header fields <strong>must</strong> contain only 46 * US-ASCII characters. If a header contains non US-ASCII characters, 47 * it must be encoded as per the rules in RFC 2047. The MimeUtility 48 * class provided in this package can be used to to achieve this. 49 * Callers of the <code>setHeader</code>, <code>addHeader</code>, and 50 * <code>addHeaderLine</code> methods are responsible for enforcing 51 * the MIME requirements for the specified headers. In addition, these 52 * header fields must be folded (wrapped) before being sent if they 53 * exceed the line length limitation for the transport (1000 bytes for 54 * SMTP). Received headers may have been folded. The application is 55 * responsible for folding and unfolding headers as appropriate. <p> 56 * 57 * @author John Mani 58 * @author Bill Shannon 59 */ 60 final class InternetHeaders { 61 62 private final FinalArrayList<hdr> headers = new FinalArrayList<hdr>(); 63 64 /** 65 * Read and parse the given RFC822 message stream till the 66 * blank line separating the header from the body. Store the 67 * header lines inside this InternetHeaders object. <p> 68 * <p/> 69 * Note that the header lines are added into this InternetHeaders 70 * object, so any existing headers in this object will not be 71 * affected. 72 * 73 * @param lis RFC822 input stream 74 */ 75 InternetHeaders(MIMEParser.LineInputStream lis) { 76 // Read header lines until a blank line. It is valid 77 // to have BodyParts with no header lines. 78 String line; 79 String prevline = null; // the previous header line, as a string 80 // a buffer to accumulate the header in, when we know it's needed 81 StringBuffer lineBuffer = new StringBuffer(); 82 83 try { 84 //while ((line = lis.readLine()) != null) { 85 do { 86 line = lis.readLine(); 87 if (line != null && 88 (line.startsWith(" ") || line.startsWith("\t"))) { 89 // continuation of header 90 if (prevline != null) { 91 lineBuffer.append(prevline); 92 prevline = null; 93 } 94 lineBuffer.append("\r\n"); 95 lineBuffer.append(line); 96 } else { 97 // new header 98 if (prevline != null) 99 addHeaderLine(prevline); 100 else if (lineBuffer.length() > 0) { 101 // store previous header first 102 addHeaderLine(lineBuffer.toString()); 103 lineBuffer.setLength(0); 104 } 105 prevline = line; 106 } 107 } while (line != null && line.length() > 0); 108 } catch (IOException ioex) { 109 throw new MIMEParsingException("Error in input stream", ioex); 110 } 111 } 112 113 /** 114 * Return all the values for the specified header. The 115 * values are String objects. Returns <code>null</code> 116 * if no headers with the specified name exist. 117 * 118 * @param name header name 119 * @return array of header values, or null if none 120 */ 121 List<String> getHeader(String name) { 122 // XXX - should we just step through in index order? 123 FinalArrayList<String> v = new FinalArrayList<String>(); // accumulate return values 124 125 int len = headers.size(); 126 for( int i=0; i<len; i++ ) { 127 hdr h = (hdr) headers.get(i); 128 if (name.equalsIgnoreCase(h.name)) { 129 v.add(h.getValue()); 130 } 131 } 132 return (v.size() == 0) ? null : v; 133 } 134 135 /** 136 * Return all the headers as an Enumeration of 137 * {@link Header} objects. 138 * 139 * @return Header objects 140 */ 141 FinalArrayList<? extends Header> getAllHeaders() { 142 return headers; // conceptually it should be read-only, but for performance reason I'm not wrapping it here 143 } 144 145 /** 146 * Add an RFC822 header line to the header store. 147 * If the line starts with a space or tab (a continuation line), 148 * add it to the last header line in the list. <p> 149 * <p/> 150 * Note that RFC822 headers can only contain US-ASCII characters 151 * 152 * @param line raw RFC822 header line 153 */ 154 void addHeaderLine(String line) { 155 try { 156 char c = line.charAt(0); 157 if (c == ' ' || c == '\t') { 158 hdr h = (hdr) headers.get(headers.size() - 1); 159 h.line += "\r\n" + line; 160 } else 161 headers.add(new hdr(line)); 162 } catch (StringIndexOutOfBoundsException e) { 163 // line is empty, ignore it 164 return; 165 } catch (NoSuchElementException e) { 166 // XXX - vector is empty? 167 } 168 } 169 170 } 171 172 /* 173 * A private utility class to represent an individual header. 174 */ 175 176 class hdr implements Header { 177 178 String name; // the canonicalized (trimmed) name of this header 179 // XXX - should name be stored in lower case? 180 String line; // the entire RFC822 header "line" 181 182 /* 183 * Constructor that takes a line and splits out 184 * the header name. 185 */ 186 hdr(String l) { 187 int i = l.indexOf(':'); 188 if (i < 0) { 189 // should never happen 190 name = l.trim(); 191 } else { 192 name = l.substring(0, i).trim(); 193 } 194 line = l; 195 } 196 197 /* 198 * Constructor that takes a header name and value. 199 */ 200 hdr(String n, String v) { 201 name = n; 202 line = n + ": " + v; 203 } 204 205 /* 206 * Return the "name" part of the header line. 207 */ 208 public String getName() { 209 return name; 210 } 211 212 /* 213 * Return the "value" part of the header line. 214 */ 215 public String getValue() { 216 int i = line.indexOf(':'); 217 if (i < 0) 218 return line; 219 220 int j; 221 if (name.equalsIgnoreCase("Content-Description")) { 222 // Content-Description should retain the folded whitespace after header unfolding - 223 // rf. RFC2822 section 2.2.3, rf. RFC2822 section 3.2.3 224 for (j = i + 1; j < line.length(); j++) { 225 char c = line.charAt(j); 226 if (!(/*c == ' ' ||*/c == '\t' || c == '\r' || c == '\n')) 227 break; 228 } 229 } else { 230 // skip whitespace after ':' 231 for (j = i + 1; j < line.length(); j++) { 232 char c = line.charAt(j); 233 if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n')) 234 break; 235 } 236 } 237 return line.substring(j); 238 } 239 }