All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.mail.internet.InternetHeaders Maven / Gradle / Ivy

Go to download

API for Google App Engine standard environment with some of the dependencies shaded (repackaged)

There is a newer version: 2.0.27
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package javax.mail.internet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.mail.Address;
import javax.mail.Header;
import javax.mail.MessagingException;

/**
 * Class that represents the RFC822 headers associated with a message.
 *
 * @version $Rev: 702234 $ $Date: 2008-10-06 14:25:41 -0500 (Mon, 06 Oct 2008) $
 */
public class InternetHeaders {
    // the list of headers (to preserve order);
    protected List headers = new ArrayList();

    private transient String lastHeaderName;

    /**
     * Create an empty InternetHeaders
     */
    public InternetHeaders() {
        // these are created in the preferred order of the headers.
        addHeader("Return-Path", null);
        addHeader("Received", null);
        addHeader("Resent-Date", null);
        addHeader("Resent-From", null);
        addHeader("Resent-Sender", null);
        addHeader("Resent-To", null);
        addHeader("Resent-Cc", null);
        addHeader("Resent-Bcc", null);
        addHeader("Resent-Message-Id", null);
        addHeader("Date", null);
        addHeader("From", null);
        addHeader("Sender", null);
        addHeader("Reply-To", null);
        addHeader("To", null);
        addHeader("Cc", null);
        addHeader("Bcc", null);
        addHeader("Message-Id", null);
        addHeader("In-Reply-To", null);
        addHeader("References", null);
        addHeader("Subject", null);
        addHeader("Comments", null);
        addHeader("Keywords", null);
        addHeader("Errors-To", null);
        addHeader("MIME-Version", null);
        addHeader("Content-Type", null);
        addHeader("Content-Transfer-Encoding", null);
        addHeader("Content-MD5", null);
        // the following is a special marker used to identify new header insertion points.
        addHeader(":", null);
        addHeader("Content-Length", null);
        addHeader("Status", null);
    }

    /**
     * Create a new InternetHeaders initialized by reading headers from the
     * stream.
     *
     * @param in
     *            the RFC822 input stream to load from
     * @throws MessagingException
     *             if there is a problem pasring the stream
     */
    public InternetHeaders(InputStream in) throws MessagingException {
        load(in);
    }

    /**
     * Read and parse the supplied stream and add all headers to the current
     * set.
     *
     * @param in
     *            the RFC822 input stream to load from
     * @throws MessagingException
     *             if there is a problem pasring the stream
     */
    public void load(InputStream in) throws MessagingException {
        try {
            StringBuffer buffer = new StringBuffer(128); 
            String line; 
            // loop until we hit the end or a null line 
            while ((line = readLine(in)) != null) {
                // lines beginning with white space get special handling 
                if (line.startsWith(" ") || line.startsWith("\t")) {
                    // this gets handled using the logic defined by 
                    // the addHeaderLine method.  If this line is a continuation, but 
                    // there's nothing before it, just call addHeaderLine to add it 
                    // to the last header in the headers list 
                    if (buffer.length() == 0) {
                        addHeaderLine(line); 
                    }
                    else {
                        // preserve the line break and append the continuation 
                        buffer.append("\r\n"); 
                        buffer.append(line); 
                    }
                }
                else {
                    // if we have a line pending in the buffer, flush it 
                    if (buffer.length() > 0) {
                        addHeaderLine(buffer.toString()); 
                        buffer.setLength(0); 
                    }
                    // add this to the accumulator 
                    buffer.append(line); 
                }
            }
            
            // if we have a line pending in the buffer, flush it 
            if (buffer.length() > 0) {
                addHeaderLine(buffer.toString()); 
            }
        } catch (IOException e) {
            throw new MessagingException("Error loading headers", e);
        }
    }
    
    
    /**
     * Read a single line from the input stream 
     * 
     * @param in     The source stream for the line
     * 
     * @return The string value of the line (without line separators)
     */
    private String readLine(InputStream in) throws IOException {
        StringBuffer buffer = new StringBuffer(128); 
        
        int c; 
        
        while ((c = in.read()) != -1) {
            // a linefeed is a terminator, always.  
            if (c == '\n') {
                break; 
            }
            // just ignore the CR.  The next character SHOULD be an NL.  If not, we're 
            // just going to discard this 
            else if (c == '\r') {
                continue; 
            }
            else {
                // just add to the buffer 
                buffer.append((char)c); 
            }
        }
        
        // no characters found...this was either an eof or a null line. 
        if (buffer.length() == 0) {
            return null; 
        }
        
        return buffer.toString(); 
    }


    /**
     * Return all the values for the specified header.
     *
     * @param name
     *            the header to return
     * @return the values for that header, or null if the header is not present
     */
    public String[] getHeader(String name) {
        List accumulator = new ArrayList();

        for (int i = 0; i < headers.size(); i++) {
            InternetHeader header = (InternetHeader)headers.get(i);
            if (header.getName().equalsIgnoreCase(name) && header.getValue() != null) {
                accumulator.add(header.getValue());
            }
        }

        // this is defined as returning null of nothing is found.
        if (accumulator.isEmpty()) {
            return null;
        }

        // convert this to an array.
        return (String[])accumulator.toArray(new String[accumulator.size()]);
    }

    /**
     * Return the values for the specified header as a single String. If the
     * header has more than one value then all values are concatenated together
     * separated by the supplied delimiter.
     *
     * @param name
     *            the header to return
     * @param delimiter
     *            the delimiter used in concatenation
     * @return the header as a single String
     */
    public String getHeader(String name, String delimiter) {
        // get all of the headers with this name
        String[] matches = getHeader(name);

        // no match?  return a null.
        if (matches == null) {
            return null;
        }

        // a null delimiter means just return the first one.  If there's only one item, this is easy too.
        if (matches.length == 1 || delimiter == null) {
            return matches[0];
        }

        // perform the concatenation
        StringBuffer result = new StringBuffer(matches[0]);

        for (int i = 1; i < matches.length; i++) {
            result.append(delimiter);
            result.append(matches[i]);
        }

        return result.toString();
    }


    /**
     * Set the value of the header to the supplied value; any existing headers
     * are removed.
     *
     * @param name
     *            the name of the header
     * @param value
     *            the new value
     */
    public void setHeader(String name, String value) {
        // look for a header match
        for (int i = 0; i < headers.size(); i++) {
            InternetHeader header = (InternetHeader)headers.get(i);
            // found a matching header
            if (name.equalsIgnoreCase(header.getName())) {
                // we update both the name and the value for a set so that 
                // the header ends up with the same case as what is getting set
                header.setValue(value);
                header.setName(name); 
                // remove all of the headers from this point
                removeHeaders(name, i + 1);
                return;
            }
        }

        // doesn't exist, so process as an add.
        addHeader(name, value);
    }


    /**
     * Remove all headers with the given name, starting with the
     * specified start position.
     *
     * @param name   The target header name.
     * @param pos    The position of the first header to examine.
     */
    private void removeHeaders(String name, int pos) {
        // now go remove all other instances of this header
        for (int i = pos; i < headers.size(); i++) {
            InternetHeader header = (InternetHeader)headers.get(i);
            // found a matching header
            if (name.equalsIgnoreCase(header.getName())) {
                // remove this item, and back up
                headers.remove(i);
                i--;
            }
        }
    }


    /**
     * Find a header in the current list by name, returning the index.
     *
     * @param name   The target name.
     *
     * @return The index of the header in the list.  Returns -1 for a not found
     *         condition.
     */
    private int findHeader(String name) {
        return findHeader(name, 0);
    }


    /**
     * Find a header in the current list, beginning with the specified
     * start index.
     *
     * @param name   The target header name.
     * @param start  The search start index.
     *
     * @return The index of the first matching header.  Returns -1 if the
     *         header is not located.
     */
    private int findHeader(String name, int start) {
        for (int i = start; i < headers.size(); i++) {
            InternetHeader header = (InternetHeader)headers.get(i);
            // found a matching header
            if (name.equalsIgnoreCase(header.getName())) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Add a new value to the header with the supplied name.
     *
     * @param name
     *            the name of the header to add a new value for
     * @param value
     *            another value
     */
    public void addHeader(String name, String value) {
        InternetHeader newHeader = new InternetHeader(name, value);

        // The javamail spec states that "Recieved" headers need to be added in reverse order.
        // Return-Path is permitted before Received, so handle it the same way.
        if (name.equalsIgnoreCase("Received") || name.equalsIgnoreCase("Return-Path")) {
            // see if we have one of these already
            int pos = findHeader(name);

            // either insert before an existing header, or insert at the very beginning
            if (pos != -1) {
                // this could be a placeholder header with a null value.  If it is, just update
                // the value.  Otherwise, insert in front of the existing header.
                InternetHeader oldHeader = (InternetHeader)headers.get(pos);
                if (oldHeader.getValue() == null) {
                    oldHeader.setValue(value);
                }
                else {
                    headers.add(pos, newHeader);
                }
            }
            else {
                // doesn't exist, so insert at the beginning
                headers.add(0, newHeader);
            }
        }
        // normal insertion
        else {
            // see if we have one of these already
            int pos = findHeader(name);

            // either insert before an existing header, or insert at the very beginning
            if (pos != -1) {
                InternetHeader oldHeader = (InternetHeader)headers.get(pos);
                // if the existing header is a place holder, we can just update the value
                if (oldHeader.getValue() == null) {
                    oldHeader.setValue(value);
                }
                else {
                    // we have at least one existing header with this name.  We need to find the last occurrance,
                    // and insert after that spot.

                    int lastPos = findHeader(name, pos + 1);

                    while (lastPos != -1) {
                        pos = lastPos;
                        lastPos = findHeader(name, pos + 1);
                    }

                    // ok, we have the insertion position
                    headers.add(pos + 1, newHeader);
                }
            }
            else {
                // find the insertion marker.  If that is missing somehow, insert at the end.
                pos = findHeader(":");
                if (pos == -1) {
                    pos = headers.size();
                }
                headers.add(pos, newHeader);
            }
        }
    }


    /**
     * Remove all header entries with the supplied name
     *
     * @param name
     *            the header to remove
     */
    public void removeHeader(String name) {
        // the first occurrance of a header is just zeroed out.
        int pos = findHeader(name);

        if (pos != -1) {
            InternetHeader oldHeader = (InternetHeader)headers.get(pos);
            // keep the header in the list, but with a null value
            oldHeader.setValue(null);
            // now remove all other headers with this name
            removeHeaders(name, pos + 1);
        }
    }


    /**
     * Return all headers.
     *
     * @return an Enumeration
containing all headers */ public Enumeration getAllHeaders() { List result = new ArrayList(); for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { result.add(header); } } // just return a list enumerator for the header list. return Collections.enumeration(result); } /** * Test if a given header name is a match for any header in the * given list. * * @param name The name of the current tested header. * @param names The list of names to match against. * * @return True if this is a match for any name in the list, false * for a complete mismatch. */ private boolean matchHeader(String name, String[] names) { // the list of names is not required, so treat this as if it // was an empty list and we didn't get a match. if (names == null) { return false; } for (int i = 0; i < names.length; i++) { if (name.equalsIgnoreCase(names[i])) { return true; } } return false; } /** * Return all matching Header objects. */ public Enumeration getMatchingHeaders(String[] names) { List result = new ArrayList(); for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { // only add the matching ones if (matchHeader(header.getName(), names)) { result.add(header); } } } return Collections.enumeration(result); } /** * Return all non matching Header objects. */ public Enumeration getNonMatchingHeaders(String[] names) { List result = new ArrayList(); for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { // only add the non-matching ones if (!matchHeader(header.getName(), names)) { result.add(header); } } } return Collections.enumeration(result); } /** * Add an RFC822 header line to the header store. If the line starts with a * space or tab (a continuation line), add it to the last header line in the * list. Otherwise, append the new header line to the list. * * Note that RFC822 headers can only contain US-ASCII characters * * @param line * raw RFC822 header line */ public void addHeaderLine(String line) { // null lines are a nop if (line.length() == 0) { return; } // we need to test the first character to see if this is a continuation whitespace char ch = line.charAt(0); // tabs and spaces are special. This is a continuation of the last header in the list. if (ch == ' ' || ch == '\t') { int size = headers.size(); // it's possible that we have a leading blank line. if (size > 0) { InternetHeader header = (InternetHeader)headers.get(size - 1); header.appendValue(line); } } else { // this just gets appended to the end, preserving the addition order. headers.add(new InternetHeader(line)); } } /** * Return all the header lines as an Enumeration of Strings. */ public Enumeration getAllHeaderLines() { return new HeaderLineEnumeration(getAllHeaders()); } /** * Return all matching header lines as an Enumeration of Strings. */ public Enumeration getMatchingHeaderLines(String[] names) { return new HeaderLineEnumeration(getMatchingHeaders(names)); } /** * Return all non-matching header lines. */ public Enumeration getNonMatchingHeaderLines(String[] names) { return new HeaderLineEnumeration(getNonMatchingHeaders(names)); } /** * Set an internet header from a list of addresses. The * first address item is set, followed by a series of addHeaders(). * * @param name The name to set. * @param addresses The list of addresses to set. */ void setHeader(String name, Address[] addresses) { // if this is empty, then we need to replace this if (addresses.length == 0) { removeHeader(name); } else { // replace the first header setHeader(name, addresses[0].toString()); // now add the rest as extra headers. for (int i = 1; i < addresses.length; i++) { Address address = addresses[i]; addHeader(name, address.toString()); } } } /** * Write out the set of headers, except for any * headers specified in the optional ignore list. * * @param out The output stream. * @param ignore The optional ignore list. * * @exception IOException */ void writeTo(OutputStream out, String[] ignore) throws IOException { if (ignore == null) { // write out all header lines with non-null values for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { header.writeTo(out); } } } else { // write out all matching header lines with non-null values for (int i = 0; i < headers.size(); i++) { InternetHeader header = (InternetHeader)headers.get(i); // we only include headers with real values, no placeholders if (header.getValue() != null) { if (!matchHeader(header.getName(), ignore)) { header.writeTo(out); } } } } } protected static final class InternetHeader extends Header { public InternetHeader(String h) { // initialize with null values, which we'll update once we parse the string super("", ""); int separator = h.indexOf(':'); // no separator, then we take this as a name with a null string value. if (separator == -1) { name = h.trim(); } else { name = h.substring(0, separator); // step past the separator. Now we need to remove any leading white space characters. separator++; while (separator < h.length()) { char ch = h.charAt(separator); if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') { break; } separator++; } value = h.substring(separator); } } public InternetHeader(String name, String value) { super(name, value); } /** * Package scope method for setting the header value. * * @param value The new header value. */ void setValue(String value) { this.value = value; } /** * Package scope method for setting the name value. * * @param name The new header name */ void setName(String name) { this.name = name; } /** * Package scope method for extending a header value. * * @param value The appended header value. */ void appendValue(String value) { if (this.value == null) { this.value = value; } else { this.value = this.value + "\r\n" + value; } } void writeTo(OutputStream out) throws IOException { out.write(name.getBytes()); out.write(':'); out.write(' '); out.write(value.getBytes()); out.write('\r'); out.write('\n'); } } private static class HeaderLineEnumeration implements Enumeration { private Enumeration headers; public HeaderLineEnumeration(Enumeration headers) { this.headers = headers; } public boolean hasMoreElements() { return headers.hasMoreElements(); } public Object nextElement() { Header h = (Header) headers.nextElement(); return h.getName() + ": " + h.getValue(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy