Save This Page
Home » glassfish-v2ur2-b04-src » javax » mail » internet » [javadoc | source]
    1   /*
    2    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    3    *
    4    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
    5    *
    6    * The contents of this file are subject to the terms of either the GNU
    7    * General Public License Version 2 only ("GPL") or the Common Development
    8    * and Distribution License("CDDL") (collectively, the "License").  You
    9    * may not use this file except in compliance with the License. You can obtain
   10    * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
   11    * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
   12    * language governing permissions and limitations under the License.
   13    *
   14    * When distributing the software, include this License Header Notice in each
   15    * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
   16    * Sun designates this particular file as subject to the "Classpath" exception
   17    * as provided by Sun in the GPL Version 2 section of the License file that
   18    * accompanied this code.  If applicable, add the following below the License
   19    * Header, with the fields enclosed by brackets [] replaced by your own
   20    * identifying information: "Portions Copyrighted [year]
   21    * [name of copyright owner]"
   22    *
   23    * Contributor(s):
   24    *
   25    * If you wish your version of this file to be governed by only the CDDL or
   26    * only the GPL Version 2, indicate your decision by adding "[Contributor]
   27    * elects to include this software in this distribution under the [CDDL or GPL
   28    * Version 2] license."  If you don't indicate a single choice of license, a
   29    * recipient has the option to distribute your version of this file under
   30    * either the CDDL, the GPL Version 2 or to extend the choice of license to
   31    * its licensees as provided above.  However, if you add GPL Version 2 code
   32    * and therefore, elected the GPL Version 2 license, then the option applies
   33    * only if the new code is made subject to such option by the copyright
   34    * holder.
   35    */
   36   
   37   /*
   38    * @(#)InternetHeaders.java	1.22 07/05/04
   39    */
   40   
   41   package javax.mail.internet;
   42   
   43   import java.io;
   44   import java.util;
   45   import javax.mail;
   46   import com.sun.mail.util.LineInputStream;
   47   
   48   /**
   49    * InternetHeaders is a utility class that manages RFC822 style
   50    * headers. Given an RFC822 format message stream, it reads lines
   51    * until the blank line that indicates end of header. The input stream
   52    * is positioned at the start of the body. The lines are stored 
   53    * within the object and can be extracted as either Strings or
   54    * {@link javax.mail.Header} objects. <p>
   55    *
   56    * This class is mostly intended for service providers. MimeMessage
   57    * and MimeBody use this class for holding their headers. <p>
   58    * 
   59    * <hr> <strong>A note on RFC822 and MIME headers</strong><p>
   60    *
   61    * RFC822 and MIME header fields <strong>must</strong> contain only 
   62    * US-ASCII characters. If a header contains non US-ASCII characters,
   63    * it must be encoded as per the rules in RFC 2047. The MimeUtility
   64    * class provided in this package can be used to to achieve this. 
   65    * Callers of the <code>setHeader</code>, <code>addHeader</code>, and
   66    * <code>addHeaderLine</code> methods are responsible for enforcing
   67    * the MIME requirements for the specified headers.  In addition, these
   68    * header fields must be folded (wrapped) before being sent if they
   69    * exceed the line length limitation for the transport (1000 bytes for
   70    * SMTP).  Received headers may have been folded.  The application is
   71    * responsible for folding and unfolding headers as appropriate. <p>
   72    *
   73    * @see	javax.mail.internet.MimeUtility
   74    * @author John Mani
   75    * @author Bill Shannon
   76    */
   77   
   78   public class InternetHeaders {
   79       /**
   80        * An individual internet header.  This class is only used by
   81        * subclasses of InternetHeaders. <p>
   82        *
   83        * An InternetHeader object with a null value is used as a placeholder
   84        * for headers of that name, to preserve the order of headers.
   85        * A placeholder InternetHeader object with a name of ":" marks
   86        * the location in the list of headers where new headers are
   87        * added by default.
   88        *
   89        * @since	JavaMail 1.4
   90        */
   91       protected static final class InternetHeader extends Header {
   92   	/*
   93   	 * Note that the value field from the superclass
   94   	 * isn't used in this class.  We extract the value
   95   	 * from the line field as needed.  We store the line
   96   	 * rather than just the value to ensure that we can
   97   	 * get back the exact original line, with the original
   98   	 * whitespace, etc.
   99   	 */
  100   	String line;    // the entire RFC822 header "line",
  101   			// or null if placeholder
  102   
  103   	/**
  104   	 * Constructor that takes a line and splits out
  105   	 * the header name.
  106   	 */
  107   	public InternetHeader(String l) {
  108   	    super("", "");	// XXX - we'll change it later
  109   	    int i = l.indexOf(':');
  110   	    if (i < 0) {
  111   		// should never happen
  112   		name = l.trim();
  113   	    } else {
  114   		name = l.substring(0, i).trim();
  115   	    }
  116   	    line = l;
  117   	}
  118   
  119   	/**
  120   	 * Constructor that takes a header name and value.
  121   	 */
  122   	public InternetHeader(String n, String v) {
  123   	    super(n, "");
  124   	    if (v != null)
  125   		line = n + ": " + v;
  126   	    else
  127   		line = null;
  128   	}
  129   
  130   	/**
  131   	 * Return the "value" part of the header line.
  132   	 */
  133   	public String getValue() {
  134   	    int i = line.indexOf(':');
  135   	    if (i < 0)
  136   		return line;
  137   	    // skip whitespace after ':'
  138   	    int j;
  139   	    for (j = i + 1; j < line.length(); j++) {
  140   		char c = line.charAt(j);
  141   		if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n'))
  142   		    break;
  143   	    }
  144   	    return line.substring(j);
  145   	}
  146       }
  147   
  148       /*
  149        * The enumeration object used to enumerate an
  150        * InternetHeaders object.  Can return
  151        * either a String or a Header object.
  152        */
  153       static class matchEnum implements Enumeration {
  154   	private Iterator e;	// enum object of headers List
  155   	// XXX - is this overkill?  should we step through in index
  156   	// order instead?
  157   	private String names[];	// names to match, or not
  158   	private boolean match;	// return matching headers?
  159   	private boolean want_line;	// return header lines?
  160   	private InternetHeader next_header; // the next header to be returned
  161   
  162   	/*
  163   	 * Constructor.  Initialize the enumeration for the entire
  164   	 * List of headers, the set of headers, whether to return
  165   	 * matching or non-matching headers, and whether to return
  166   	 * header lines or Header objects.
  167   	 */
  168   	matchEnum(List v, String n[], boolean m, boolean l) {
  169   	    e = v.iterator();
  170   	    names = n;
  171   	    match = m;
  172   	    want_line = l;
  173   	    next_header = null;
  174   	}
  175   
  176   	/*
  177   	 * Any more elements in this enumeration?
  178   	 */
  179   	public boolean hasMoreElements() {
  180   	    // if necessary, prefetch the next matching header,
  181   	    // and remember it.
  182   	    if (next_header == null)
  183   		next_header = nextMatch();
  184   	    return next_header != null;
  185   	}
  186   
  187   	/*
  188   	 * Return the next element.
  189   	 */
  190   	public Object nextElement() {
  191   	    if (next_header == null)
  192   		next_header = nextMatch();
  193   
  194   	    if (next_header == null)
  195   		throw new NoSuchElementException("No more headers");
  196   
  197   	    InternetHeader h = next_header;
  198   	    next_header = null;
  199   	    if (want_line)
  200   		return h.line;
  201   	    else
  202   		return new Header(h.getName(), h.getValue());
  203   	}
  204   
  205   	/*
  206   	 * Return the next Header object according to the match
  207   	 * criteria, or null if none left.
  208   	 */
  209   	private InternetHeader nextMatch() {
  210   	    next:
  211   	    while (e.hasNext()) {
  212   		InternetHeader h = (InternetHeader)e.next();
  213   
  214   		// skip "place holder" headers
  215   		if (h.line == null)
  216   		    continue;
  217   
  218   		// if no names to match against, return appropriately
  219   		if (names == null)
  220   		    return match ? null : h;
  221   
  222   		// check whether this header matches any of the names
  223   		for (int i = 0; i < names.length; i++) {
  224   		    if (names[i].equalsIgnoreCase(h.getName())) {
  225   			if (match)
  226   			    return h;
  227   			else
  228   			    // found a match, but we're
  229   			    // looking for non-matches.
  230   			    // try next header.
  231   			    continue next;
  232   		    }
  233   		}
  234   		// found no matches.  if that's what we wanted, return it.
  235   		if (!match)
  236   		    return h;
  237   	    }
  238   	    return null;
  239   	}
  240       }
  241   
  242   
  243       /**
  244        * The actual list of Headers, including placeholder entries.
  245        * Placeholder entries are Headers with a null value and
  246        * are never seen by clients of the InternetHeaders class.
  247        * Placeholder entries are used to keep track of the preferred
  248        * order of headers.  Headers are never actually removed from
  249        * the list, they're converted into placeholder entries.
  250        * New headers are added after existing headers of the same name
  251        * (or before in the case of <code>Received</code> and
  252        * <code>Return-Path</code> headers).  If no existing header
  253        * or placeholder for the header is found, new headers are
  254        * added after the special placeholder with the name ":".
  255        *
  256        * @since	JavaMail 1.4
  257        */
  258       protected List headers;
  259   
  260       /**
  261        * Create an empty InternetHeaders object.  Placeholder entries
  262        * are inserted to indicate the preferred order of headers.
  263        */
  264       public InternetHeaders() { 
  265      	headers = new ArrayList(40); 
  266   	headers.add(new InternetHeader("Return-Path", null));
  267   	headers.add(new InternetHeader("Received", null));
  268   	headers.add(new InternetHeader("Resent-Date", null));
  269   	headers.add(new InternetHeader("Resent-From", null));
  270   	headers.add(new InternetHeader("Resent-Sender", null));
  271   	headers.add(new InternetHeader("Resent-To", null));
  272   	headers.add(new InternetHeader("Resent-Cc", null));
  273   	headers.add(new InternetHeader("Resent-Bcc", null));
  274   	headers.add(new InternetHeader("Resent-Message-Id", null));
  275   	headers.add(new InternetHeader("Date", null));
  276   	headers.add(new InternetHeader("From", null));
  277   	headers.add(new InternetHeader("Sender", null));
  278   	headers.add(new InternetHeader("Reply-To", null));
  279   	headers.add(new InternetHeader("To", null));
  280   	headers.add(new InternetHeader("Cc", null));
  281   	headers.add(new InternetHeader("Bcc", null));
  282   	headers.add(new InternetHeader("Message-Id", null));
  283   	headers.add(new InternetHeader("In-Reply-To", null));
  284   	headers.add(new InternetHeader("References", null));
  285   	headers.add(new InternetHeader("Subject", null));
  286   	headers.add(new InternetHeader("Comments", null));
  287   	headers.add(new InternetHeader("Keywords", null));
  288   	headers.add(new InternetHeader("Errors-To", null));
  289   	headers.add(new InternetHeader("MIME-Version", null));
  290   	headers.add(new InternetHeader("Content-Type", null));
  291   	headers.add(new InternetHeader("Content-Transfer-Encoding", null));
  292   	headers.add(new InternetHeader("Content-MD5", null));
  293   	headers.add(new InternetHeader(":", null));
  294   	headers.add(new InternetHeader("Content-Length", null));
  295   	headers.add(new InternetHeader("Status", null));
  296       }
  297   
  298       /**
  299        * Read and parse the given RFC822 message stream till the 
  300        * blank line separating the header from the body. The input 
  301        * stream is left positioned at the start of the body. The 
  302        * header lines are stored internally. <p>
  303        *
  304        * For efficiency, wrap a BufferedInputStream around the actual
  305        * input stream and pass it as the parameter. <p>
  306        *
  307        * No placeholder entries are inserted; the original order of
  308        * the headers is preserved.
  309        *
  310        * @param	is 	RFC822 input stream
  311        */
  312       public InternetHeaders(InputStream is) throws MessagingException {
  313      	headers = new ArrayList(40); 
  314   	load(is);
  315       }
  316   
  317       /**
  318        * Read and parse the given RFC822 message stream till the
  319        * blank line separating the header from the body. Store the
  320        * header lines inside this InternetHeaders object. The order
  321        * of header lines is preserved. <p>
  322        *
  323        * Note that the header lines are added into this InternetHeaders
  324        * object, so any existing headers in this object will not be
  325        * affected.  Headers are added to the end of the existing list
  326        * of headers, in order.
  327        *
  328        * @param	is 	RFC822 input stream
  329        */
  330       public void load(InputStream is) throws MessagingException {
  331   	// Read header lines until a blank line. It is valid
  332   	// to have BodyParts with no header lines.
  333   	String line;
  334   	LineInputStream lis = new LineInputStream(is);
  335   	String prevline = null;	// the previous header line, as a string
  336   	// a buffer to accumulate the header in, when we know it's needed
  337   	StringBuffer lineBuffer = new StringBuffer();
  338   
  339   	try {
  340   	    //while ((line = lis.readLine()) != null) {
  341   	    do {
  342   		line = lis.readLine();
  343   		if (line != null &&
  344   			(line.startsWith(" ") || line.startsWith("\t"))) {
  345   		    // continuation of header
  346   		    if (prevline != null) {
  347   			lineBuffer.append(prevline);
  348   			prevline = null;
  349   		    }
  350   		    lineBuffer.append("\r\n");
  351   		    lineBuffer.append(line);
  352   		} else {
  353   		    // new header
  354   		    if (prevline != null)
  355   			addHeaderLine(prevline);
  356   		    else if (lineBuffer.length() > 0) {
  357   			// store previous header first
  358   			addHeaderLine(lineBuffer.toString());
  359   			lineBuffer.setLength(0);
  360   		    }
  361   		    prevline = line;
  362   		}
  363   	    } while (line != null && line.length() > 0);
  364   	} catch (IOException ioex) {
  365   	    throw new MessagingException("Error in input stream", ioex);
  366   	}
  367       }
  368   
  369       /**
  370        * Return all the values for the specified header. The
  371        * values are String objects.  Returns <code>null</code>
  372        * if no headers with the specified name exist.
  373        *
  374        * @param	name 	header name
  375        * @return		array of header values, or null if none
  376        */
  377       public String[] getHeader(String name) {
  378   	Iterator e = headers.iterator();
  379   	// XXX - should we just step through in index order?
  380   	List v = new ArrayList(); // accumulate return values
  381   
  382   	while (e.hasNext()) {
  383   	    InternetHeader h = (InternetHeader)e.next();
  384   	    if (name.equalsIgnoreCase(h.getName()) && h.line != null) {
  385   		v.add(h.getValue());
  386   	    }
  387   	}
  388   	if (v.size() == 0)
  389   	    return (null);
  390   	// convert List to an array for return
  391   	String r[] = new String[v.size()];
  392   	r = (String[])v.toArray(r);
  393   	return (r);
  394       }
  395   
  396       /**
  397        * Get all the headers for this header name, returned as a single
  398        * String, with headers separated by the delimiter. If the
  399        * delimiter is <code>null</code>, only the first header is 
  400        * returned.  Returns <code>null</code>
  401        * if no headers with the specified name exist.
  402        *
  403        * @param	name 		header name
  404        * @param   delimiter	delimiter
  405        * @return                  the value fields for all headers with
  406        *				this name, or null if none
  407        */
  408       public String getHeader(String name, String delimiter) {
  409   	String s[] = getHeader(name);
  410   
  411   	if (s == null)
  412   	    return null;
  413   	
  414   	if ((s.length == 1) || delimiter == null)
  415   	    return s[0];
  416   	
  417   	StringBuffer r = new StringBuffer(s[0]);
  418   	for (int i = 1; i < s.length; i++) {
  419   	    r.append(delimiter);
  420   	    r.append(s[i]);
  421   	}
  422   	return r.toString();
  423       }
  424   
  425       /**
  426        * Change the first header line that matches name
  427        * to have value, adding a new header if no existing header
  428        * matches. Remove all matching headers but the first. <p>
  429        *
  430        * Note that RFC822 headers can only contain US-ASCII characters
  431        *
  432        * @param	name	header name
  433        * @param	value	header value
  434        */
  435       public void setHeader(String name, String value) {
  436   	boolean found = false;
  437   
  438   	for (int i = 0; i < headers.size(); i++) {
  439   	    InternetHeader h = (InternetHeader)headers.get(i);
  440   	    if (name.equalsIgnoreCase(h.getName())) {
  441   		if (!found) {
  442   		    int j;
  443   		    if (h.line != null && (j = h.line.indexOf(':')) >= 0) {
  444   			h.line = h.line.substring(0, j + 1) + " " + value;
  445   			// preserves capitalization, spacing
  446   		    } else {
  447   			h.line = name + ": " + value;
  448   		    }
  449   		    found = true;
  450   		} else {
  451   		    headers.remove(i);
  452   		    i--;    // have to look at i again
  453   		}
  454   	    }
  455   	}
  456       
  457   	if (!found) {
  458   	    addHeader(name, value);
  459   	}
  460       }
  461   
  462       /**
  463        * Add a header with the specified name and value to the header list. <p>
  464        *
  465        * The current implementation knows about the preferred order of most
  466        * well-known headers and will insert headers in that order.  In
  467        * addition, it knows that <code>Received</code> headers should be
  468        * inserted in reverse order (newest before oldest), and that they
  469        * should appear at the beginning of the headers, preceeded only by
  470        * a possible <code>Return-Path</code> header.  <p>
  471        *
  472        * Note that RFC822 headers can only contain US-ASCII characters.
  473        *
  474        * @param	name	header name
  475        * @param	value	header value
  476        */ 
  477       public void addHeader(String name, String value) {
  478   	int pos = headers.size();
  479   	boolean addReverse =
  480   	    name.equalsIgnoreCase("Received") ||
  481   	    name.equalsIgnoreCase("Return-Path");
  482   	if (addReverse)
  483   	    pos = 0;
  484   	for (int i = headers.size() - 1; i >= 0; i--) {
  485   	    InternetHeader h = (InternetHeader)headers.get(i);
  486   	    if (name.equalsIgnoreCase(h.getName())) {
  487   		if (addReverse) {
  488   		    pos = i;
  489   		} else {
  490   		    headers.add(i + 1, new InternetHeader(name, value));
  491   		    return;
  492   		}
  493   	    }
  494   	    // marker for default place to add new headers
  495   	    if (h.getName().equals(":"))
  496   		pos = i;
  497   	}
  498   	headers.add(pos, new InternetHeader(name, value));
  499       }
  500   
  501       /**
  502        * Remove all header entries that match the given name
  503        * @param	name 	header name
  504        */
  505       public void removeHeader(String name) { 
  506   	for (int i = 0; i < headers.size(); i++) {
  507   	    InternetHeader h = (InternetHeader)headers.get(i);
  508   	    if (name.equalsIgnoreCase(h.getName())) {
  509   		h.line = null;
  510   		//headers.remove(i);
  511   		//i--;    // have to look at i again
  512   	    }
  513   	}
  514       }
  515   
  516       /**
  517        * Return all the headers as an Enumeration of
  518        * {@link javax.mail.Header} objects.
  519        *
  520        * @return	Header objects	
  521        */
  522       public Enumeration getAllHeaders() {
  523   	return (new matchEnum(headers, null, false, false));
  524       }
  525   
  526       /**
  527        * Return all matching {@link javax.mail.Header} objects.
  528        *
  529        * @return	matching Header objects	
  530        */
  531       public Enumeration getMatchingHeaders(String[] names) {
  532   	return (new matchEnum(headers, names, true, false));
  533       }
  534   
  535       /**
  536        * Return all non-matching {@link javax.mail.Header} objects.
  537        *
  538        * @return	non-matching Header objects	
  539        */
  540       public Enumeration getNonMatchingHeaders(String[] names) {
  541   	return (new matchEnum(headers, names, false, false));
  542       }
  543   
  544       /**
  545        * Add an RFC822 header line to the header store.
  546        * If the line starts with a space or tab (a continuation line),
  547        * add it to the last header line in the list.  Otherwise,
  548        * append the new header line to the list.  <p>
  549        *
  550        * Note that RFC822 headers can only contain US-ASCII characters
  551        *
  552        * @param	line	raw RFC822 header line
  553        */
  554       public void addHeaderLine(String line) {
  555   	try {
  556   	    char c = line.charAt(0);
  557   	    if (c == ' ' || c == '\t') {
  558   		InternetHeader h =
  559   		    (InternetHeader)headers.get(headers.size() - 1);
  560   		h.line += "\r\n" + line;
  561   	    } else
  562   		headers.add(new InternetHeader(line));
  563   	} catch (StringIndexOutOfBoundsException e) {
  564   	    // line is empty, ignore it
  565   	    return;
  566   	} catch (NoSuchElementException e) {
  567   	    // XXX - vector is empty?
  568   	}
  569       }
  570   
  571       /**
  572        * Return all the header lines as an Enumeration of Strings.
  573        */
  574       public Enumeration getAllHeaderLines() { 
  575   	return (getNonMatchingHeaderLines(null));
  576       }
  577   
  578       /**
  579        * Return all matching header lines as an Enumeration of Strings.
  580        */
  581       public Enumeration getMatchingHeaderLines(String[] names) {
  582   	return (new matchEnum(headers, names, true, true));	
  583       }
  584   
  585       /**
  586        * Return all non-matching header lines
  587        */
  588       public Enumeration getNonMatchingHeaderLines(String[] names) {
  589   	return (new matchEnum(headers, names, false, true));
  590       }
  591   }

Save This Page
Home » glassfish-v2ur2-b04-src » javax » mail » internet » [javadoc | source]