You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ol...@apache.org on 2004/06/05 18:49:21 UTC

cvs commit: jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/cookie TestCookieCompatibilitySpec.java TestCookieRFC2109Spec.java

olegk       2004/06/05 09:49:21

  Modified:    httpclient/src/java/org/apache/commons/httpclient
                        Cookie.java
               httpclient/src/java/org/apache/commons/httpclient/cookie
                        RFC2109Spec.java
               httpclient/src/test/org/apache/commons/httpclient/cookie
                        TestCookieCompatibilitySpec.java
                        TestCookieRFC2109Spec.java
  Log:
  PR #29377 (Cookies with names containing blanks or starting with $ should be rejected by RFC2109 spec only)
  
  Contributed by Oleg Kalnichevski
  Reviewed by Michael Becke
  
  Revision  Changes    Path
  1.44      +549 -555  jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Cookie.java
  
  Index: Cookie.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Cookie.java,v
  retrieving revision 1.43
  retrieving revision 1.44
  diff -u -r1.43 -r1.44
  --- Cookie.java	18 Apr 2004 23:51:34 -0000	1.43
  +++ Cookie.java	5 Jun 2004 16:49:20 -0000	1.44
  @@ -1,555 +1,549 @@
  -/*
  - * $Header$
  - * $Revision$
  - * $Date$
  - *
  - * ====================================================================
  - *
  - *  Copyright 1999-2004 The Apache Software Foundation
  - *
  - *  Licensed 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.
  - * ====================================================================
  - *
  - * This software consists of voluntary contributions made by many
  - * individuals on behalf of the Apache Software Foundation.  For more
  - * information on the Apache Software Foundation, please see
  - * <http://www.apache.org/>.
  - *
  - */
  -
  -package org.apache.commons.httpclient;
  -
  -import java.io.Serializable;
  -import java.text.RuleBasedCollator;
  -import java.util.Comparator;
  -import java.util.Date;
  -import java.util.Locale;
  -
  -import org.apache.commons.httpclient.cookie.CookiePolicy;
  -import org.apache.commons.httpclient.cookie.CookieSpec;
  -import org.apache.commons.logging.Log;
  -import org.apache.commons.logging.LogFactory;
  -
  -/**
  - * <p>
  - * HTTP "magic-cookie" represents a piece of state information
  - * that the HTTP agent and the target server can exchange to maintain 
  - * a session.
  - * </p>
  - * 
  - * @author B.C. Holmes
  - * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
  - * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
  - * @author Rod Waldhoff
  - * @author dIon Gillard
  - * @author Sean C. Sullivan
  - * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
  - * @author Marc A. Saegesser
  - * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
  - * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
  - * 
  - * @version $Revision$ $Date$
  - */
  -public class Cookie extends NameValuePair implements Serializable, Comparator {
  -
  -    // ----------------------------------------------------------- Constructors
  -
  -    /**
  -     * Default constructor. Creates a blank cookie 
  -     */
  -
  -    public Cookie() {
  -        this(null, "noname", null, null, null, false);
  -    }
  -
  -    /**
  -     * Creates a cookie with the given name, value and domain attribute.
  -     *
  -     * @param name    the cookie name
  -     * @param value   the cookie value
  -     * @param domain  the domain this cookie can be sent to
  -     */
  -    public Cookie(String domain, String name, String value) {
  -        this(domain, name, value, null, null, false);
  -    }
  -
  -    /**
  -     * Creates a cookie with the given name, value, domain attribute,
  -     * path attribute, expiration attribute, and secure attribute 
  -     *
  -     * @param name    the cookie name
  -     * @param value   the cookie value
  -     * @param domain  the domain this cookie can be sent to
  -     * @param path    the path prefix for which this cookie can be sent
  -     * @param expires the {@link Date} at which this cookie expires,
  -     *                or <tt>null</tt> if the cookie expires at the end
  -     *                of the session
  -     * @param secure if true this cookie can only be sent over secure
  -     * connections
  -     * @throws IllegalArgumentException If cookie name is null or blank,
  -     *   cookie name contains a blank, or cookie name starts with character $
  -     *   
  -     */
  -    public Cookie(String domain, String name, String value, 
  -        String path, Date expires, boolean secure) {
  -            
  -        super(name, value);
  -        LOG.trace("enter Cookie(String, String, String, String, Date, boolean)");
  -        if (name == null) {
  -            throw new IllegalArgumentException("Cookie name may not be null");
  -        }
  -        if (name.equals("")) {
  -            throw new IllegalArgumentException("Cookie name may not be blank");
  -        }
  -        if (name.indexOf(' ') != -1) {
  -            throw new IllegalArgumentException("Cookie name may not contain blanks");
  -        }
  -        if (name.startsWith("$")) {
  -            throw new IllegalArgumentException("Cookie name may not start with $");
  -        }
  -        this.setPath(path);
  -        this.setDomain(domain);
  -        this.setExpiryDate(expires);
  -        this.setSecure(secure);
  -    }
  -
  -    /**
  -     * Creates a cookie with the given name, value, domain attribute,
  -     * path attribute, maximum age attribute, and secure attribute 
  -     *
  -     * @param name   the cookie name
  -     * @param value  the cookie value
  -     * @param domain the domain this cookie can be sent to
  -     * @param path   the path prefix for which this cookie can be sent
  -     * @param maxAge the number of seconds for which this cookie is valid.
  -     *               maxAge is expected to be a non-negative number. 
  -     *               <tt>-1</tt> signifies that the cookie should never expire.
  -     * @param secure if <tt>true</tt> this cookie can only be sent over secure
  -     * connections
  -     */
  -    public Cookie(String domain, String name, String value, String path, 
  -        int maxAge, boolean secure) {
  -            
  -        this(domain, name, value, path, null, secure);
  -        if (maxAge < -1) {
  -            throw new IllegalArgumentException("Invalid max age:  " + Integer.toString(maxAge));
  -        }            
  -        if (maxAge >= 0) {
  -            setExpiryDate(new Date(System.currentTimeMillis() + maxAge * 1000L));
  -        }
  -    }
  -
  -    /**
  -     * Returns the comment describing the purpose of this cookie, or
  -     * <tt>null</tt> if no such comment has been defined.
  -     * 
  -     * @return comment 
  -     *
  -     * @see #setComment(String)
  -     */
  -    public String getComment() {
  -        return cookieComment;
  -    }
  -
  -    /**
  -     * If a user agent (web browser) presents this cookie to a user, the
  -     * cookie's purpose will be described using this comment.
  -     * 
  -     * @param comment
  -     *  
  -     * @see #getComment()
  -     */
  -    public void setComment(String comment) {
  -        cookieComment = comment;
  -    }
  -
  -    /**
  -     * Returns the expiration {@link Date} of the cookie, or <tt>null</tt>
  -     * if none exists.
  -     * <p><strong>Note:</strong> the object returned by this method is 
  -     * considered immutable. Changing it (e.g. using setTime()) could result
  -     * in undefined behaviour. Do so at your peril. </p>
  -     * @return Expiration {@link Date}, or <tt>null</tt>.
  -     *
  -     * @see #setExpiryDate(java.util.Date)
  -     *
  -     */
  -    public Date getExpiryDate() {
  -        return cookieExpiryDate;
  -    }
  -
  -    /**
  -     * Sets expiration date.
  -     * <p><strong>Note:</strong> the object returned by this method is considered
  -     * immutable. Changing it (e.g. using setTime()) could result in undefined 
  -     * behaviour. Do so at your peril.</p>
  -     *
  -     * @param expiryDate the {@link Date} after which this cookie is no longer valid.
  -     *
  -     * @see #getExpiryDate
  -     *
  -     */
  -    public void setExpiryDate (Date expiryDate) {
  -        cookieExpiryDate = expiryDate;
  -    }
  -
  -
  -    /**
  -     * Returns <tt>false</tt> if the cookie should be discarded at the end
  -     * of the "session"; <tt>true</tt> otherwise.
  -     *
  -     * @return <tt>false</tt> if the cookie should be discarded at the end
  -     *         of the "session"; <tt>true</tt> otherwise
  -     */
  -    public boolean isPersistent() {
  -        return (null != cookieExpiryDate);
  -    }
  -
  -
  -    /**
  -     * Returns domain attribute of the cookie.
  -     * 
  -     * @return the value of the domain attribute
  -     *
  -     * @see #setDomain(java.lang.String)
  -     */
  -    public String getDomain() {
  -        return cookieDomain;
  -    }
  -
  -    /**
  -     * Sets the domain attribute.
  -     * 
  -     * @param domain The value of the domain attribute
  -     *
  -     * @see #getDomain
  -     */
  -    public void setDomain(String domain) {
  -        if (domain != null) {
  -            int ndx = domain.indexOf(":");
  -            if (ndx != -1) {
  -              domain = domain.substring(0, ndx);
  -            }
  -            cookieDomain = domain.toLowerCase();
  -        }
  -    }
  -
  -
  -    /**
  -     * Returns the path attribute of the cookie
  -     * 
  -     * @return The value of the path attribute.
  -     * 
  -     * @see #setPath(java.lang.String)
  -     */
  -    public String getPath() {
  -        return cookiePath;
  -    }
  -
  -    /**
  -     * Sets the path attribute.
  -     *
  -     * @param path The value of the path attribute
  -     *
  -     * @see #getPath
  -     *
  -     */
  -    public void setPath(String path) {
  -        cookiePath = path;
  -    }
  -
  -    /**
  -     * @return <code>true</code> if this cookie should only be sent over secure connections.
  -     * @see #setSecure(boolean)
  -     */
  -    public boolean getSecure() {
  -        return isSecure;
  -    }
  -
  -    /**
  -     * Sets the secure attribute of the cookie.
  -     * <p>
  -     * When <tt>true</tt> the cookie should only be sent
  -     * using a secure protocol (https).  This should only be set when
  -     * the cookie's originating server used a secure protocol to set the
  -     * cookie's value.
  -     *
  -     * @param secure The value of the secure attribute
  -     * 
  -     * @see #getSecure()
  -     */
  -    public void setSecure (boolean secure) {
  -        isSecure = secure;
  -    }
  -
  -    /**
  -     * Returns the version of the cookie specification to which this
  -     * cookie conforms.
  -     *
  -     * @return the version of the cookie.
  -     * 
  -     * @see #setVersion(int)
  -     *
  -     */
  -    public int getVersion() {
  -        return cookieVersion;
  -    }
  -
  -    /**
  -     * Sets the version of the cookie specification to which this
  -     * cookie conforms. 
  -     *
  -     * @param version the version of the cookie.
  -     * 
  -     * @see #getVersion
  -     */
  -    public void setVersion(int version) {
  -        cookieVersion = version;
  -    }
  -
  -    /**
  -     * Returns true if this cookie has expired.
  -     * 
  -     * @return <tt>true</tt> if the cookie has expired.
  -     */
  -    public boolean isExpired() {
  -        return (cookieExpiryDate != null  
  -            && cookieExpiryDate.getTime() <= System.currentTimeMillis());
  -    }
  -
  -    /**
  -     * Returns true if this cookie has expired according to the time passed in.
  -     * 
  -     * @param now The current time.
  -     * 
  -     * @return <tt>true</tt> if the cookie expired.
  -     */
  -    public boolean isExpired(Date now) {
  -        return (cookieExpiryDate != null  
  -            && cookieExpiryDate.getTime() <= now.getTime());
  -    }
  -
  -
  -    /**
  -     * Indicates whether the cookie had a path specified in a 
  -     * path attribute of the <tt>Set-Cookie</tt> header. This value
  -     * is important for generating the <tt>Cookie</tt> header because 
  -     * some cookie specifications require that the <tt>Cookie</tt> header 
  -     * should only include a path attribute if the cookie's path 
  -     * was specified in the <tt>Set-Cookie</tt> header.
  -     *
  -     * @param value <tt>true</tt> if the cookie's path was explicitly 
  -     * set, <tt>false</tt> otherwise.
  -     * 
  -     * @see #isPathAttributeSpecified
  -     */
  -    public void setPathAttributeSpecified(boolean value) {
  -        hasPathAttribute = value;
  -    }
  -
  -    /**
  -     * Returns <tt>true</tt> if cookie's path was set via a path attribute
  -     * in the <tt>Set-Cookie</tt> header.
  -     *
  -     * @return value <tt>true</tt> if the cookie's path was explicitly 
  -     * set, <tt>false</tt> otherwise.
  -     * 
  -     * @see #setPathAttributeSpecified
  -     */
  -    public boolean isPathAttributeSpecified() {
  -        return hasPathAttribute;
  -    }
  -
  -    /**
  -     * Indicates whether the cookie had a domain specified in a 
  -     * domain attribute of the <tt>Set-Cookie</tt> header. This value
  -     * is important for generating the <tt>Cookie</tt> header because 
  -     * some cookie specifications require that the <tt>Cookie</tt> header 
  -     * should only include a domain attribute if the cookie's domain 
  -     * was specified in the <tt>Set-Cookie</tt> header.
  -     *
  -     * @param value <tt>true</tt> if the cookie's domain was explicitly 
  -     * set, <tt>false</tt> otherwise.
  -     *
  -     * @see #isDomainAttributeSpecified
  -     */
  -    public void setDomainAttributeSpecified(boolean value) {
  -        hasDomainAttribute = value;
  -    }
  -
  -    /**
  -     * Returns <tt>true</tt> if cookie's domain was set via a domain 
  -     * attribute in the <tt>Set-Cookie</tt> header.
  -     *
  -     * @return value <tt>true</tt> if the cookie's domain was explicitly 
  -     * set, <tt>false</tt> otherwise.
  -     *
  -     * @see #setDomainAttributeSpecified
  -     */
  -    public boolean isDomainAttributeSpecified() {
  -        return hasDomainAttribute;
  -    }
  -
  -    /**
  -     * Returns a hash code in keeping with the
  -     * {@link Object#hashCode} general hashCode contract.
  -     * @return A hash code
  -     */
  -    public int hashCode() {
  -        return super.hashCode()
  -            ^ (null == cookiePath ? 0 : cookiePath.hashCode())
  -            ^ (null == cookieDomain ? 0 : cookieDomain.hashCode());
  -    }
  -
  -
  -    /**
  -     * Two cookies are equal if the name, path and domain match.
  -     * @param obj The object to compare against.
  -     * @return true if the two objects are equal.
  -     */
  -    public boolean equals(Object obj) {
  -        LOG.trace("enter Cookie.equals(Object)");
  -        
  -        if ((obj != null) && (obj instanceof Cookie)) {
  -            Cookie that = (Cookie) obj;
  -            return 
  -                (null == this.getName() 
  -                    ? null == that.getName() 
  -                    : this.getName().equals(that.getName())) 
  -                && (null == this.getPath() 
  -                    ? null == that.getPath() 
  -                    : this.getPath().equals(that.getPath())) 
  -                && (null == this.getDomain() 
  -                    ? null == that.getDomain() 
  -                    : this.getDomain().equals(that.getDomain()));
  -        } else {
  -            return false;
  -        }
  -    }
  -
  -
  -    /**
  -     * Return a textual representation of the cookie.
  -     * 
  -     * @return string.
  -     */
  -    public String toExternalForm() {
  -        CookieSpec spec = null;
  -        if (getVersion() > 0) {
  -            spec = CookiePolicy.getDefaultSpec(); 
  -        } else {
  -            spec = CookiePolicy.getCookieSpec(CookiePolicy.NETSCAPE); 
  -        }
  -        return spec.formatCookie(this); 
  -    }
  -
  -    /**
  -     * <p>Compares two cookies to determine order for cookie header.</p>
  -     * <p>Most specific should be first. </p>
  -     * <p>This method is implemented so a cookie can be used as a comparator for
  -     * a SortedSet of cookies. Specifically it's used above in the 
  -     * createCookieHeader method.</p>
  -     * @param o1 The first object to be compared
  -     * @param o2 The second object to be compared
  -     * @return See {@link java.util.Comparator#compare(Object,Object)}
  -     */
  -    public int compare(Object o1, Object o2) {
  -        LOG.trace("enter Cookie.compare(Object, Object)");
  -
  -        if (!(o1 instanceof Cookie)) {
  -            throw new ClassCastException(o1.getClass().getName());
  -        }
  -        if (!(o2 instanceof Cookie)) {
  -            throw new ClassCastException(o2.getClass().getName());
  -        }
  -        Cookie c1 = (Cookie) o1;
  -        Cookie c2 = (Cookie) o2;
  -        if (c1.getPath() == null && c2.getPath() == null) {
  -            return 0;
  -        } else if (c1.getPath() == null) {
  -            // null is assumed to be "/"
  -            if (c2.getPath().equals(CookieSpec.PATH_DELIM)) {
  -                return 0;
  -            } else {
  -                return -1;
  -            }
  -        } else if (c2.getPath() == null) {
  -            // null is assumed to be "/"
  -            if (c1.getPath().equals(CookieSpec.PATH_DELIM)) {
  -                return 0;
  -            } else {
  -                return 1;
  -            }
  -        } else {
  -            return STRING_COLLATOR.compare(c1.getPath(), c2.getPath());
  -        }
  -    }
  -
  -    /**
  -     * Return a textual representation of the cookie.
  -     * 
  -     * @return string.
  -     * 
  -     * @see #toExternalForm
  -     */
  -    public String toString() {
  -        return toExternalForm();
  -    }
  -
  -   // ----------------------------------------------------- Instance Variables
  -
  -   /** Comment attribute. */
  -   private String  cookieComment;
  -
  -   /** Domain attribute. */
  -   private String  cookieDomain;
  -
  -   /** Expiration {@link Date}. */
  -   private Date    cookieExpiryDate;
  -
  -   /** Path attribute. */
  -   private String  cookiePath;
  -
  -   /** My secure flag. */
  -   private boolean isSecure;
  -
  -   /**
  -    * Specifies if the set-cookie header included a Path attribute for this
  -    * cookie
  -    */
  -   private boolean hasPathAttribute = false;
  -
  -   /**
  -    * Specifies if the set-cookie header included a Domain attribute for this
  -    * cookie
  -    */
  -   private boolean hasDomainAttribute = false;
  -
  -   /** The version of the cookie specification I was created from. */
  -   private int     cookieVersion = 0;
  -
  -   // -------------------------------------------------------------- Constants
  -
  -   /** 
  -    * Collator for Cookie comparisons.  Could be replaced with references to
  -    * specific Locales.
  -    */
  -   private static final RuleBasedCollator STRING_COLLATOR =
  -        (RuleBasedCollator) RuleBasedCollator.getInstance(
  -                                                new Locale("en", "US", ""));
  -
  -   /** Log object for this class */
  -   private static final Log LOG = LogFactory.getLog(Cookie.class);
  -
  -}
  -
  +/*
  + * $Header$
  + * $Revision$
  + * $Date$
  + *
  + * ====================================================================
  + *
  + *  Copyright 1999-2004 The Apache Software Foundation
  + *
  + *  Licensed 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.
  + * ====================================================================
  + *
  + * This software consists of voluntary contributions made by many
  + * individuals on behalf of the Apache Software Foundation.  For more
  + * information on the Apache Software Foundation, please see
  + * <http://www.apache.org/>.
  + *
  + */
  +
  +package org.apache.commons.httpclient;
  +
  +import java.io.Serializable;
  +import java.text.RuleBasedCollator;
  +import java.util.Comparator;
  +import java.util.Date;
  +import java.util.Locale;
  +
  +import org.apache.commons.httpclient.cookie.CookiePolicy;
  +import org.apache.commons.httpclient.cookie.CookieSpec;
  +import org.apache.commons.logging.Log;
  +import org.apache.commons.logging.LogFactory;
  +
  +/**
  + * <p>
  + * HTTP "magic-cookie" represents a piece of state information
  + * that the HTTP agent and the target server can exchange to maintain 
  + * a session.
  + * </p>
  + * 
  + * @author B.C. Holmes
  + * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
  + * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
  + * @author Rod Waldhoff
  + * @author dIon Gillard
  + * @author Sean C. Sullivan
  + * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
  + * @author Marc A. Saegesser
  + * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
  + * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
  + * 
  + * @version $Revision$ $Date$
  + */
  +public class Cookie extends NameValuePair implements Serializable, Comparator {
  +
  +    // ----------------------------------------------------------- Constructors
  +
  +    /**
  +     * Default constructor. Creates a blank cookie 
  +     */
  +
  +    public Cookie() {
  +        this(null, "noname", null, null, null, false);
  +    }
  +
  +    /**
  +     * Creates a cookie with the given name, value and domain attribute.
  +     *
  +     * @param name    the cookie name
  +     * @param value   the cookie value
  +     * @param domain  the domain this cookie can be sent to
  +     */
  +    public Cookie(String domain, String name, String value) {
  +        this(domain, name, value, null, null, false);
  +    }
  +
  +    /**
  +     * Creates a cookie with the given name, value, domain attribute,
  +     * path attribute, expiration attribute, and secure attribute 
  +     *
  +     * @param name    the cookie name
  +     * @param value   the cookie value
  +     * @param domain  the domain this cookie can be sent to
  +     * @param path    the path prefix for which this cookie can be sent
  +     * @param expires the {@link Date} at which this cookie expires,
  +     *                or <tt>null</tt> if the cookie expires at the end
  +     *                of the session
  +     * @param secure if true this cookie can only be sent over secure
  +     * connections
  +     * @throws IllegalArgumentException If cookie name is null or blank,
  +     *   cookie name contains a blank, or cookie name starts with character $
  +     *   
  +     */
  +    public Cookie(String domain, String name, String value, 
  +        String path, Date expires, boolean secure) {
  +            
  +        super(name, value);
  +        LOG.trace("enter Cookie(String, String, String, String, Date, boolean)");
  +        if (name == null) {
  +            throw new IllegalArgumentException("Cookie name may not be null");
  +        }
  +        if (name.trim().equals("")) {
  +            throw new IllegalArgumentException("Cookie name may not be blank");
  +        }
  +        this.setPath(path);
  +        this.setDomain(domain);
  +        this.setExpiryDate(expires);
  +        this.setSecure(secure);
  +    }
  +
  +    /**
  +     * Creates a cookie with the given name, value, domain attribute,
  +     * path attribute, maximum age attribute, and secure attribute 
  +     *
  +     * @param name   the cookie name
  +     * @param value  the cookie value
  +     * @param domain the domain this cookie can be sent to
  +     * @param path   the path prefix for which this cookie can be sent
  +     * @param maxAge the number of seconds for which this cookie is valid.
  +     *               maxAge is expected to be a non-negative number. 
  +     *               <tt>-1</tt> signifies that the cookie should never expire.
  +     * @param secure if <tt>true</tt> this cookie can only be sent over secure
  +     * connections
  +     */
  +    public Cookie(String domain, String name, String value, String path, 
  +        int maxAge, boolean secure) {
  +            
  +        this(domain, name, value, path, null, secure);
  +        if (maxAge < -1) {
  +            throw new IllegalArgumentException("Invalid max age:  " + Integer.toString(maxAge));
  +        }            
  +        if (maxAge >= 0) {
  +            setExpiryDate(new Date(System.currentTimeMillis() + maxAge * 1000L));
  +        }
  +    }
  +
  +    /**
  +     * Returns the comment describing the purpose of this cookie, or
  +     * <tt>null</tt> if no such comment has been defined.
  +     * 
  +     * @return comment 
  +     *
  +     * @see #setComment(String)
  +     */
  +    public String getComment() {
  +        return cookieComment;
  +    }
  +
  +    /**
  +     * If a user agent (web browser) presents this cookie to a user, the
  +     * cookie's purpose will be described using this comment.
  +     * 
  +     * @param comment
  +     *  
  +     * @see #getComment()
  +     */
  +    public void setComment(String comment) {
  +        cookieComment = comment;
  +    }
  +
  +    /**
  +     * Returns the expiration {@link Date} of the cookie, or <tt>null</tt>
  +     * if none exists.
  +     * <p><strong>Note:</strong> the object returned by this method is 
  +     * considered immutable. Changing it (e.g. using setTime()) could result
  +     * in undefined behaviour. Do so at your peril. </p>
  +     * @return Expiration {@link Date}, or <tt>null</tt>.
  +     *
  +     * @see #setExpiryDate(java.util.Date)
  +     *
  +     */
  +    public Date getExpiryDate() {
  +        return cookieExpiryDate;
  +    }
  +
  +    /**
  +     * Sets expiration date.
  +     * <p><strong>Note:</strong> the object returned by this method is considered
  +     * immutable. Changing it (e.g. using setTime()) could result in undefined 
  +     * behaviour. Do so at your peril.</p>
  +     *
  +     * @param expiryDate the {@link Date} after which this cookie is no longer valid.
  +     *
  +     * @see #getExpiryDate
  +     *
  +     */
  +    public void setExpiryDate (Date expiryDate) {
  +        cookieExpiryDate = expiryDate;
  +    }
  +
  +
  +    /**
  +     * Returns <tt>false</tt> if the cookie should be discarded at the end
  +     * of the "session"; <tt>true</tt> otherwise.
  +     *
  +     * @return <tt>false</tt> if the cookie should be discarded at the end
  +     *         of the "session"; <tt>true</tt> otherwise
  +     */
  +    public boolean isPersistent() {
  +        return (null != cookieExpiryDate);
  +    }
  +
  +
  +    /**
  +     * Returns domain attribute of the cookie.
  +     * 
  +     * @return the value of the domain attribute
  +     *
  +     * @see #setDomain(java.lang.String)
  +     */
  +    public String getDomain() {
  +        return cookieDomain;
  +    }
  +
  +    /**
  +     * Sets the domain attribute.
  +     * 
  +     * @param domain The value of the domain attribute
  +     *
  +     * @see #getDomain
  +     */
  +    public void setDomain(String domain) {
  +        if (domain != null) {
  +            int ndx = domain.indexOf(":");
  +            if (ndx != -1) {
  +              domain = domain.substring(0, ndx);
  +            }
  +            cookieDomain = domain.toLowerCase();
  +        }
  +    }
  +
  +
  +    /**
  +     * Returns the path attribute of the cookie
  +     * 
  +     * @return The value of the path attribute.
  +     * 
  +     * @see #setPath(java.lang.String)
  +     */
  +    public String getPath() {
  +        return cookiePath;
  +    }
  +
  +    /**
  +     * Sets the path attribute.
  +     *
  +     * @param path The value of the path attribute
  +     *
  +     * @see #getPath
  +     *
  +     */
  +    public void setPath(String path) {
  +        cookiePath = path;
  +    }
  +
  +    /**
  +     * @return <code>true</code> if this cookie should only be sent over secure connections.
  +     * @see #setSecure(boolean)
  +     */
  +    public boolean getSecure() {
  +        return isSecure;
  +    }
  +
  +    /**
  +     * Sets the secure attribute of the cookie.
  +     * <p>
  +     * When <tt>true</tt> the cookie should only be sent
  +     * using a secure protocol (https).  This should only be set when
  +     * the cookie's originating server used a secure protocol to set the
  +     * cookie's value.
  +     *
  +     * @param secure The value of the secure attribute
  +     * 
  +     * @see #getSecure()
  +     */
  +    public void setSecure (boolean secure) {
  +        isSecure = secure;
  +    }
  +
  +    /**
  +     * Returns the version of the cookie specification to which this
  +     * cookie conforms.
  +     *
  +     * @return the version of the cookie.
  +     * 
  +     * @see #setVersion(int)
  +     *
  +     */
  +    public int getVersion() {
  +        return cookieVersion;
  +    }
  +
  +    /**
  +     * Sets the version of the cookie specification to which this
  +     * cookie conforms. 
  +     *
  +     * @param version the version of the cookie.
  +     * 
  +     * @see #getVersion
  +     */
  +    public void setVersion(int version) {
  +        cookieVersion = version;
  +    }
  +
  +    /**
  +     * Returns true if this cookie has expired.
  +     * 
  +     * @return <tt>true</tt> if the cookie has expired.
  +     */
  +    public boolean isExpired() {
  +        return (cookieExpiryDate != null  
  +            && cookieExpiryDate.getTime() <= System.currentTimeMillis());
  +    }
  +
  +    /**
  +     * Returns true if this cookie has expired according to the time passed in.
  +     * 
  +     * @param now The current time.
  +     * 
  +     * @return <tt>true</tt> if the cookie expired.
  +     */
  +    public boolean isExpired(Date now) {
  +        return (cookieExpiryDate != null  
  +            && cookieExpiryDate.getTime() <= now.getTime());
  +    }
  +
  +
  +    /**
  +     * Indicates whether the cookie had a path specified in a 
  +     * path attribute of the <tt>Set-Cookie</tt> header. This value
  +     * is important for generating the <tt>Cookie</tt> header because 
  +     * some cookie specifications require that the <tt>Cookie</tt> header 
  +     * should only include a path attribute if the cookie's path 
  +     * was specified in the <tt>Set-Cookie</tt> header.
  +     *
  +     * @param value <tt>true</tt> if the cookie's path was explicitly 
  +     * set, <tt>false</tt> otherwise.
  +     * 
  +     * @see #isPathAttributeSpecified
  +     */
  +    public void setPathAttributeSpecified(boolean value) {
  +        hasPathAttribute = value;
  +    }
  +
  +    /**
  +     * Returns <tt>true</tt> if cookie's path was set via a path attribute
  +     * in the <tt>Set-Cookie</tt> header.
  +     *
  +     * @return value <tt>true</tt> if the cookie's path was explicitly 
  +     * set, <tt>false</tt> otherwise.
  +     * 
  +     * @see #setPathAttributeSpecified
  +     */
  +    public boolean isPathAttributeSpecified() {
  +        return hasPathAttribute;
  +    }
  +
  +    /**
  +     * Indicates whether the cookie had a domain specified in a 
  +     * domain attribute of the <tt>Set-Cookie</tt> header. This value
  +     * is important for generating the <tt>Cookie</tt> header because 
  +     * some cookie specifications require that the <tt>Cookie</tt> header 
  +     * should only include a domain attribute if the cookie's domain 
  +     * was specified in the <tt>Set-Cookie</tt> header.
  +     *
  +     * @param value <tt>true</tt> if the cookie's domain was explicitly 
  +     * set, <tt>false</tt> otherwise.
  +     *
  +     * @see #isDomainAttributeSpecified
  +     */
  +    public void setDomainAttributeSpecified(boolean value) {
  +        hasDomainAttribute = value;
  +    }
  +
  +    /**
  +     * Returns <tt>true</tt> if cookie's domain was set via a domain 
  +     * attribute in the <tt>Set-Cookie</tt> header.
  +     *
  +     * @return value <tt>true</tt> if the cookie's domain was explicitly 
  +     * set, <tt>false</tt> otherwise.
  +     *
  +     * @see #setDomainAttributeSpecified
  +     */
  +    public boolean isDomainAttributeSpecified() {
  +        return hasDomainAttribute;
  +    }
  +
  +    /**
  +     * Returns a hash code in keeping with the
  +     * {@link Object#hashCode} general hashCode contract.
  +     * @return A hash code
  +     */
  +    public int hashCode() {
  +        return super.hashCode()
  +            ^ (null == cookiePath ? 0 : cookiePath.hashCode())
  +            ^ (null == cookieDomain ? 0 : cookieDomain.hashCode());
  +    }
  +
  +
  +    /**
  +     * Two cookies are equal if the name, path and domain match.
  +     * @param obj The object to compare against.
  +     * @return true if the two objects are equal.
  +     */
  +    public boolean equals(Object obj) {
  +        LOG.trace("enter Cookie.equals(Object)");
  +        
  +        if ((obj != null) && (obj instanceof Cookie)) {
  +            Cookie that = (Cookie) obj;
  +            return 
  +                (null == this.getName() 
  +                    ? null == that.getName() 
  +                    : this.getName().equals(that.getName())) 
  +                && (null == this.getPath() 
  +                    ? null == that.getPath() 
  +                    : this.getPath().equals(that.getPath())) 
  +                && (null == this.getDomain() 
  +                    ? null == that.getDomain() 
  +                    : this.getDomain().equals(that.getDomain()));
  +        } else {
  +            return false;
  +        }
  +    }
  +
  +
  +    /**
  +     * Return a textual representation of the cookie.
  +     * 
  +     * @return string.
  +     */
  +    public String toExternalForm() {
  +        CookieSpec spec = null;
  +        if (getVersion() > 0) {
  +            spec = CookiePolicy.getDefaultSpec(); 
  +        } else {
  +            spec = CookiePolicy.getCookieSpec(CookiePolicy.NETSCAPE); 
  +        }
  +        return spec.formatCookie(this); 
  +    }
  +
  +    /**
  +     * <p>Compares two cookies to determine order for cookie header.</p>
  +     * <p>Most specific should be first. </p>
  +     * <p>This method is implemented so a cookie can be used as a comparator for
  +     * a SortedSet of cookies. Specifically it's used above in the 
  +     * createCookieHeader method.</p>
  +     * @param o1 The first object to be compared
  +     * @param o2 The second object to be compared
  +     * @return See {@link java.util.Comparator#compare(Object,Object)}
  +     */
  +    public int compare(Object o1, Object o2) {
  +        LOG.trace("enter Cookie.compare(Object, Object)");
  +
  +        if (!(o1 instanceof Cookie)) {
  +            throw new ClassCastException(o1.getClass().getName());
  +        }
  +        if (!(o2 instanceof Cookie)) {
  +            throw new ClassCastException(o2.getClass().getName());
  +        }
  +        Cookie c1 = (Cookie) o1;
  +        Cookie c2 = (Cookie) o2;
  +        if (c1.getPath() == null && c2.getPath() == null) {
  +            return 0;
  +        } else if (c1.getPath() == null) {
  +            // null is assumed to be "/"
  +            if (c2.getPath().equals(CookieSpec.PATH_DELIM)) {
  +                return 0;
  +            } else {
  +                return -1;
  +            }
  +        } else if (c2.getPath() == null) {
  +            // null is assumed to be "/"
  +            if (c1.getPath().equals(CookieSpec.PATH_DELIM)) {
  +                return 0;
  +            } else {
  +                return 1;
  +            }
  +        } else {
  +            return STRING_COLLATOR.compare(c1.getPath(), c2.getPath());
  +        }
  +    }
  +
  +    /**
  +     * Return a textual representation of the cookie.
  +     * 
  +     * @return string.
  +     * 
  +     * @see #toExternalForm
  +     */
  +    public String toString() {
  +        return toExternalForm();
  +    }
  +
  +   // ----------------------------------------------------- Instance Variables
  +
  +   /** Comment attribute. */
  +   private String  cookieComment;
  +
  +   /** Domain attribute. */
  +   private String  cookieDomain;
  +
  +   /** Expiration {@link Date}. */
  +   private Date    cookieExpiryDate;
  +
  +   /** Path attribute. */
  +   private String  cookiePath;
  +
  +   /** My secure flag. */
  +   private boolean isSecure;
  +
  +   /**
  +    * Specifies if the set-cookie header included a Path attribute for this
  +    * cookie
  +    */
  +   private boolean hasPathAttribute = false;
  +
  +   /**
  +    * Specifies if the set-cookie header included a Domain attribute for this
  +    * cookie
  +    */
  +   private boolean hasDomainAttribute = false;
  +
  +   /** The version of the cookie specification I was created from. */
  +   private int     cookieVersion = 0;
  +
  +   // -------------------------------------------------------------- Constants
  +
  +   /** 
  +    * Collator for Cookie comparisons.  Could be replaced with references to
  +    * specific Locales.
  +    */
  +   private static final RuleBasedCollator STRING_COLLATOR =
  +        (RuleBasedCollator) RuleBasedCollator.getInstance(
  +                                                new Locale("en", "US", ""));
  +
  +   /** Log object for this class */
  +   private static final Log LOG = LogFactory.getLog(Cookie.class);
  +
  +}
  +
  
  
  
  1.21      +293 -285  jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/cookie/RFC2109Spec.java
  
  Index: RFC2109Spec.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/cookie/RFC2109Spec.java,v
  retrieving revision 1.20
  retrieving revision 1.21
  diff -u -r1.20 -r1.21
  --- RFC2109Spec.java	13 May 2004 04:02:00 -0000	1.20
  +++ RFC2109Spec.java	5 Jun 2004 16:49:20 -0000	1.21
  @@ -1,285 +1,293 @@
  -/*
  - * $Header$
  - * $Revision$
  - * $Date$
  - * 
  - * ====================================================================
  - *
  - *  Copyright 2002-2004 The Apache Software Foundation
  - *
  - *  Licensed 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.
  - * ====================================================================
  - *
  - * This software consists of voluntary contributions made by many
  - * individuals on behalf of the Apache Software Foundation.  For more
  - * information on the Apache Software Foundation, please see
  - * <http://www.apache.org/>.
  - *
  - */
  -
  -package org.apache.commons.httpclient.cookie;
  -
  -import org.apache.commons.httpclient.NameValuePair;
  -import org.apache.commons.httpclient.Cookie;
  -
  -/**
  - * <p>RFC 2109 specific cookie management functions
  - *
  - * @author  B.C. Holmes
  - * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
  - * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
  - * @author Rod Waldhoff
  - * @author dIon Gillard
  - * @author Sean C. Sullivan
  - * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
  - * @author Marc A. Saegesser
  - * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
  - * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
  - * 
  - * @since 2.0 
  - */
  -
  -public class RFC2109Spec extends CookieSpecBase {
  -
  -    /** Default constructor */    
  -    public RFC2109Spec() {
  -        super();
  -    }
  -
  -
  -    /**
  -      * Parse RFC 2109 specific cookie attribute and update the corresponsing
  -      * {@link Cookie} properties.
  -      *
  -      * @param attribute {@link NameValuePair} cookie attribute from the
  -      * <tt>Set- Cookie</tt>
  -      * @param cookie {@link Cookie} to be updated
  -      * @throws MalformedCookieException if an exception occurs during parsing
  -      */
  -    public void parseAttribute(
  -        final NameValuePair attribute, final Cookie cookie)
  -        throws MalformedCookieException {
  -          
  -        if (attribute == null) {
  -            throw new IllegalArgumentException("Attribute may not be null.");
  -        }
  -        if (cookie == null) {
  -            throw new IllegalArgumentException("Cookie may not be null.");
  -        }
  -        final String paramName = attribute.getName().toLowerCase();
  -        final String paramValue = attribute.getValue();
  -
  -        if (paramName.equals("path")) {
  -            if (paramValue == null) {
  -                throw new MalformedCookieException(
  -                    "Missing value for path attribute");
  -            }
  -            if (paramValue.trim().equals("")) {
  -                throw new MalformedCookieException(
  -                    "Blank value for path attribute");
  -            }
  -            cookie.setPath(paramValue);
  -            cookie.setPathAttributeSpecified(true);
  -        } else if (paramName.equals("version")) {
  -
  -            if (paramValue == null) {
  -                throw new MalformedCookieException(
  -                    "Missing value for version attribute");
  -            }
  -            try {
  -               cookie.setVersion(Integer.parseInt(paramValue));
  -            } catch (NumberFormatException e) {
  -                throw new MalformedCookieException("Invalid version: " 
  -                    + e.getMessage());
  -            }
  -
  -        } else {
  -            super.parseAttribute(attribute, cookie);
  -        }
  -    }
  -
  -    /**
  -      * Performs RFC 2109 compliant {@link Cookie} validation
  -      *
  -      * @param host the host from which the {@link Cookie} was received
  -      * @param port the port from which the {@link Cookie} was received
  -      * @param path the path from which the {@link Cookie} was received
  -      * @param secure <tt>true</tt> when the {@link Cookie} was received using a
  -      * secure connection
  -      * @param cookie The cookie to validate
  -      * @throws MalformedCookieException if an exception occurs during
  -      * validation
  -      */
  -    public void validate(String host, int port, String path, 
  -        boolean secure, final Cookie cookie) throws MalformedCookieException {
  -            
  -        LOG.trace("enter RFC2109Spec.validate(String, int, String, "
  -            + "boolean, Cookie)");
  -            
  -        // Perform generic validation
  -        super.validate(host, port, path, secure, cookie);
  -        // Perform RFC 2109 specific validation
  -        if (cookie.isDomainAttributeSpecified() 
  -            && (!cookie.getDomain().equals(host))) {
  -                
  -            // domain must start with dot
  -            if (!cookie.getDomain().startsWith(".")) {
  -                throw new MalformedCookieException("Domain attribute \"" 
  -                    + cookie.getDomain() 
  -                    + "\" violates RFC 2109: domain must start with a dot");
  -            }
  -            // domain must have at least one embedded dot
  -            int dotIndex = cookie.getDomain().indexOf('.', 1);
  -            if (dotIndex < 0 || dotIndex == cookie.getDomain().length() - 1) {
  -                throw new MalformedCookieException("Domain attribute \"" 
  -                    + cookie.getDomain() 
  -                    + "\" violates RFC 2109: domain must contain an embedded dot");
  -            }
  -            host = host.toLowerCase();
  -            if (!host.endsWith(cookie.getDomain())) {
  -                throw new MalformedCookieException(
  -                    "Illegal domain attribute \"" + cookie.getDomain() 
  -                    + "\". Domain of origin: \"" + host + "\"");
  -            }
  -            // host minus domain may not contain any dots
  -            String hostWithoutDomain = host.substring(0, host.length() 
  -                - cookie.getDomain().length());
  -            if (hostWithoutDomain.indexOf('.') != -1) {
  -                throw new MalformedCookieException("Domain attribute \"" 
  -                    + cookie.getDomain() 
  -                    + "\" violates RFC 2109: host minus domain may not contain any dots");
  -            }
  -        }
  -    }
  -
  -    /**
  -     * Performs domain-match as defined by the RFC2109.
  -     * @param host The target host.
  -     * @param domain The cookie domain attribute.
  -     * @return true if the specified host matches the given domain.
  -     * 
  -     * @since 3.0
  -     */
  -    public boolean domainMatch(String host, String domain) {
  -        boolean match = host.equals(domain) 
  -            || (domain.startsWith(".") && host.endsWith(domain));
  -
  -        return match;
  -    }
  -
  -    /**
  -     * Return a name/value string suitable for sending in a <tt>"Cookie"</tt>
  -     * header as defined in RFC 2109 for backward compatibility with cookie
  -     * version 0
  -     * @param name The name.
  -     * @param value The value
  -     * @param version The cookie version 
  -     * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
  -     */
  -
  -    private String formatNameValuePair(
  -        final String name, final String value, int version) {
  -            
  -        final StringBuffer buffer = new StringBuffer();
  -        if (version < 1) {
  -            buffer.append(name);
  -            buffer.append("=");
  -            if (value != null) {
  -                buffer.append(value);   
  -            }
  -        } else {
  -            buffer.append(name);
  -            buffer.append("=\"");
  -            if (value != null) {
  -                buffer.append(value);
  -            }
  -            buffer.append("\"");
  -        }
  -        return buffer.toString(); 
  -    }
  -
  -    /**
  -     * Return a string suitable for sending in a <tt>"Cookie"</tt> header 
  -     * as defined in RFC 2109 for backward compatibility with cookie version 0
  -     * @param cookie a {@link Cookie} to be formatted as string
  -     * @param version The version to use.
  -     * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
  -     */
  -    private String formatCookieAsVer(Cookie cookie, int version) {
  -        LOG.trace("enter RFC2109Spec.formatCookieAsVer(Cookie)");
  -        StringBuffer buf = new StringBuffer();
  -        buf.append(formatNameValuePair(cookie.getName(), 
  -            cookie.getValue(), version));
  -        if (cookie.getDomain() != null 
  -            && cookie.isDomainAttributeSpecified()) {
  -                
  -            buf.append("; ");
  -            buf.append(formatNameValuePair("$Domain", 
  -                cookie.getDomain(), version));
  -        }
  -        if (cookie.getPath() != null && cookie.isPathAttributeSpecified()) {
  -            buf.append("; ");
  -            buf.append(formatNameValuePair("$Path", cookie.getPath(), version));
  -        }
  -        return buf.toString();
  -    }
  -
  -
  -    /**
  -     * Return a string suitable for sending in a <tt>"Cookie"</tt> header as
  -     * defined in RFC 2109
  -     * @param cookie a {@link Cookie} to be formatted as string
  -     * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
  -     */
  -    public String formatCookie(Cookie cookie) {
  -        LOG.trace("enter RFC2109Spec.formatCookie(Cookie)");
  -        if (cookie == null) {
  -            throw new IllegalArgumentException("Cookie may not be null");
  -        }
  -        int ver = cookie.getVersion();
  -        StringBuffer buffer = new StringBuffer();
  -        buffer.append(formatNameValuePair("$Version", 
  -          Integer.toString(ver), ver));
  -        buffer.append("; ");
  -        buffer.append(formatCookieAsVer(cookie, ver));
  -        return buffer.toString();
  -    }
  -
  -    /**
  -     * Create a RFC 2109 compliant <tt>"Cookie"</tt> header value containing all
  -     * {@link Cookie}s in <i>cookies</i> suitable for sending in a <tt>"Cookie"
  -     * </tt> header
  -     * @param cookies an array of {@link Cookie}s to be formatted
  -     * @return a string suitable for sending in a Cookie header.
  -     */
  -    public String formatCookies(Cookie[] cookies) {
  -        LOG.trace("enter RFC2109Spec.formatCookieHeader(Cookie[])");
  -        int version = Integer.MAX_VALUE;
  -        // Pick the lowerest common denominator
  -        for (int i = 0; i < cookies.length; i++) {
  -            Cookie cookie = cookies[i];
  -            if (cookie.getVersion() < version) {
  -                version = cookie.getVersion();
  -            }
  -        }
  -        final StringBuffer buffer = new StringBuffer();
  -        buffer.append(formatNameValuePair("$Version", 
  -            Integer.toString(version), version));
  -        for (int i = 0; i < cookies.length; i++) {
  -            buffer.append("; ");
  -            buffer.append(formatCookieAsVer(cookies[i], version));
  -        }
  -        return buffer.toString();
  -    }
  -
  -}
  +/*
  + * $Header$
  + * $Revision$
  + * $Date$
  + * 
  + * ====================================================================
  + *
  + *  Copyright 2002-2004 The Apache Software Foundation
  + *
  + *  Licensed 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.
  + * ====================================================================
  + *
  + * This software consists of voluntary contributions made by many
  + * individuals on behalf of the Apache Software Foundation.  For more
  + * information on the Apache Software Foundation, please see
  + * <http://www.apache.org/>.
  + *
  + */
  +
  +package org.apache.commons.httpclient.cookie;
  +
  +import org.apache.commons.httpclient.NameValuePair;
  +import org.apache.commons.httpclient.Cookie;
  +
  +/**
  + * <p>RFC 2109 specific cookie management functions
  + *
  + * @author  B.C. Holmes
  + * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
  + * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
  + * @author Rod Waldhoff
  + * @author dIon Gillard
  + * @author Sean C. Sullivan
  + * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
  + * @author Marc A. Saegesser
  + * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
  + * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
  + * 
  + * @since 2.0 
  + */
  +
  +public class RFC2109Spec extends CookieSpecBase {
  +
  +    /** Default constructor */    
  +    public RFC2109Spec() {
  +        super();
  +    }
  +
  +
  +    /**
  +      * Parse RFC 2109 specific cookie attribute and update the corresponsing
  +      * {@link Cookie} properties.
  +      *
  +      * @param attribute {@link NameValuePair} cookie attribute from the
  +      * <tt>Set- Cookie</tt>
  +      * @param cookie {@link Cookie} to be updated
  +      * @throws MalformedCookieException if an exception occurs during parsing
  +      */
  +    public void parseAttribute(
  +        final NameValuePair attribute, final Cookie cookie)
  +        throws MalformedCookieException {
  +          
  +        if (attribute == null) {
  +            throw new IllegalArgumentException("Attribute may not be null.");
  +        }
  +        if (cookie == null) {
  +            throw new IllegalArgumentException("Cookie may not be null.");
  +        }
  +        final String paramName = attribute.getName().toLowerCase();
  +        final String paramValue = attribute.getValue();
  +
  +        if (paramName.equals("path")) {
  +            if (paramValue == null) {
  +                throw new MalformedCookieException(
  +                    "Missing value for path attribute");
  +            }
  +            if (paramValue.trim().equals("")) {
  +                throw new MalformedCookieException(
  +                    "Blank value for path attribute");
  +            }
  +            cookie.setPath(paramValue);
  +            cookie.setPathAttributeSpecified(true);
  +        } else if (paramName.equals("version")) {
  +
  +            if (paramValue == null) {
  +                throw new MalformedCookieException(
  +                    "Missing value for version attribute");
  +            }
  +            try {
  +               cookie.setVersion(Integer.parseInt(paramValue));
  +            } catch (NumberFormatException e) {
  +                throw new MalformedCookieException("Invalid version: " 
  +                    + e.getMessage());
  +            }
  +
  +        } else {
  +            super.parseAttribute(attribute, cookie);
  +        }
  +    }
  +
  +    /**
  +      * Performs RFC 2109 compliant {@link Cookie} validation
  +      *
  +      * @param host the host from which the {@link Cookie} was received
  +      * @param port the port from which the {@link Cookie} was received
  +      * @param path the path from which the {@link Cookie} was received
  +      * @param secure <tt>true</tt> when the {@link Cookie} was received using a
  +      * secure connection
  +      * @param cookie The cookie to validate
  +      * @throws MalformedCookieException if an exception occurs during
  +      * validation
  +      */
  +    public void validate(String host, int port, String path, 
  +        boolean secure, final Cookie cookie) throws MalformedCookieException {
  +            
  +        LOG.trace("enter RFC2109Spec.validate(String, int, String, "
  +            + "boolean, Cookie)");
  +            
  +        // Perform generic validation
  +        super.validate(host, port, path, secure, cookie);
  +        // Perform RFC 2109 specific validation
  +        
  +        if (cookie.getName().indexOf(' ') != -1) {
  +            throw new MalformedCookieException("Cookie name may not contain blanks");
  +        }
  +        if (cookie.getName().startsWith("$")) {
  +            throw new MalformedCookieException("Cookie name may not start with $");
  +        }
  +        
  +        if (cookie.isDomainAttributeSpecified() 
  +            && (!cookie.getDomain().equals(host))) {
  +                
  +            // domain must start with dot
  +            if (!cookie.getDomain().startsWith(".")) {
  +                throw new MalformedCookieException("Domain attribute \"" 
  +                    + cookie.getDomain() 
  +                    + "\" violates RFC 2109: domain must start with a dot");
  +            }
  +            // domain must have at least one embedded dot
  +            int dotIndex = cookie.getDomain().indexOf('.', 1);
  +            if (dotIndex < 0 || dotIndex == cookie.getDomain().length() - 1) {
  +                throw new MalformedCookieException("Domain attribute \"" 
  +                    + cookie.getDomain() 
  +                    + "\" violates RFC 2109: domain must contain an embedded dot");
  +            }
  +            host = host.toLowerCase();
  +            if (!host.endsWith(cookie.getDomain())) {
  +                throw new MalformedCookieException(
  +                    "Illegal domain attribute \"" + cookie.getDomain() 
  +                    + "\". Domain of origin: \"" + host + "\"");
  +            }
  +            // host minus domain may not contain any dots
  +            String hostWithoutDomain = host.substring(0, host.length() 
  +                - cookie.getDomain().length());
  +            if (hostWithoutDomain.indexOf('.') != -1) {
  +                throw new MalformedCookieException("Domain attribute \"" 
  +                    + cookie.getDomain() 
  +                    + "\" violates RFC 2109: host minus domain may not contain any dots");
  +            }
  +        }
  +    }
  +
  +    /**
  +     * Performs domain-match as defined by the RFC2109.
  +     * @param host The target host.
  +     * @param domain The cookie domain attribute.
  +     * @return true if the specified host matches the given domain.
  +     * 
  +     * @since 3.0
  +     */
  +    public boolean domainMatch(String host, String domain) {
  +        boolean match = host.equals(domain) 
  +            || (domain.startsWith(".") && host.endsWith(domain));
  +
  +        return match;
  +    }
  +
  +    /**
  +     * Return a name/value string suitable for sending in a <tt>"Cookie"</tt>
  +     * header as defined in RFC 2109 for backward compatibility with cookie
  +     * version 0
  +     * @param name The name.
  +     * @param value The value
  +     * @param version The cookie version 
  +     * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
  +     */
  +
  +    private String formatNameValuePair(
  +        final String name, final String value, int version) {
  +            
  +        final StringBuffer buffer = new StringBuffer();
  +        if (version < 1) {
  +            buffer.append(name);
  +            buffer.append("=");
  +            if (value != null) {
  +                buffer.append(value);   
  +            }
  +        } else {
  +            buffer.append(name);
  +            buffer.append("=\"");
  +            if (value != null) {
  +                buffer.append(value);
  +            }
  +            buffer.append("\"");
  +        }
  +        return buffer.toString(); 
  +    }
  +
  +    /**
  +     * Return a string suitable for sending in a <tt>"Cookie"</tt> header 
  +     * as defined in RFC 2109 for backward compatibility with cookie version 0
  +     * @param cookie a {@link Cookie} to be formatted as string
  +     * @param version The version to use.
  +     * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
  +     */
  +    private String formatCookieAsVer(Cookie cookie, int version) {
  +        LOG.trace("enter RFC2109Spec.formatCookieAsVer(Cookie)");
  +        StringBuffer buf = new StringBuffer();
  +        buf.append(formatNameValuePair(cookie.getName(), 
  +            cookie.getValue(), version));
  +        if (cookie.getDomain() != null 
  +            && cookie.isDomainAttributeSpecified()) {
  +                
  +            buf.append("; ");
  +            buf.append(formatNameValuePair("$Domain", 
  +                cookie.getDomain(), version));
  +        }
  +        if (cookie.getPath() != null && cookie.isPathAttributeSpecified()) {
  +            buf.append("; ");
  +            buf.append(formatNameValuePair("$Path", cookie.getPath(), version));
  +        }
  +        return buf.toString();
  +    }
  +
  +
  +    /**
  +     * Return a string suitable for sending in a <tt>"Cookie"</tt> header as
  +     * defined in RFC 2109
  +     * @param cookie a {@link Cookie} to be formatted as string
  +     * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
  +     */
  +    public String formatCookie(Cookie cookie) {
  +        LOG.trace("enter RFC2109Spec.formatCookie(Cookie)");
  +        if (cookie == null) {
  +            throw new IllegalArgumentException("Cookie may not be null");
  +        }
  +        int ver = cookie.getVersion();
  +        StringBuffer buffer = new StringBuffer();
  +        buffer.append(formatNameValuePair("$Version", 
  +          Integer.toString(ver), ver));
  +        buffer.append("; ");
  +        buffer.append(formatCookieAsVer(cookie, ver));
  +        return buffer.toString();
  +    }
  +
  +    /**
  +     * Create a RFC 2109 compliant <tt>"Cookie"</tt> header value containing all
  +     * {@link Cookie}s in <i>cookies</i> suitable for sending in a <tt>"Cookie"
  +     * </tt> header
  +     * @param cookies an array of {@link Cookie}s to be formatted
  +     * @return a string suitable for sending in a Cookie header.
  +     */
  +    public String formatCookies(Cookie[] cookies) {
  +        LOG.trace("enter RFC2109Spec.formatCookieHeader(Cookie[])");
  +        int version = Integer.MAX_VALUE;
  +        // Pick the lowerest common denominator
  +        for (int i = 0; i < cookies.length; i++) {
  +            Cookie cookie = cookies[i];
  +            if (cookie.getVersion() < version) {
  +                version = cookie.getVersion();
  +            }
  +        }
  +        final StringBuffer buffer = new StringBuffer();
  +        buffer.append(formatNameValuePair("$Version", 
  +            Integer.toString(version), version));
  +        for (int i = 0; i < cookies.length; i++) {
  +            buffer.append("; ");
  +            buffer.append(formatCookieAsVer(cookies[i], version));
  +        }
  +        return buffer.toString();
  +    }
  +
  +}
  
  
  
  1.6       +17 -22    jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/cookie/TestCookieCompatibilitySpec.java
  
  Index: TestCookieCompatibilitySpec.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/cookie/TestCookieCompatibilitySpec.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- TestCookieCompatibilitySpec.java	27 Apr 2004 22:35:21 -0000	1.5
  +++ TestCookieCompatibilitySpec.java	5 Jun 2004 16:49:20 -0000	1.6
  @@ -702,31 +702,26 @@
       /**
        * Tests if cookie constructor rejects cookie name containing blanks.
        */
  -    public void testInvalidCookieName() {
  -        try {
  -            CookieSpec cookiespec = new CookieSpecBase();
  -            cookiespec.parse("localhost", 80, "/", false, "invalid name="); 
  -            fail("MalformedCookieException must have been thrown");
  -        }
  -        catch(MalformedCookieException e) {
  -            // Expected            
  -        }
  +    public void testCookieNameWithBlanks() throws Exception {
  +        Header setcookie = new Header("Set-Cookie", "invalid name=");
  +        CookieSpec cookiespec = new CookieSpecBase();
  +        Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, setcookie);
  +        assertNotNull(parsed);
  +        assertEquals(1, parsed.length);
       }
   
   
       /**
        * Tests if cookie constructor rejects cookie name starting with $.
        */
  -    public void testInvalidCookieName2() {
  -        try {
  -            CookieSpec cookiespec = new CookieSpecBase();
  -            cookiespec.parse("localhost", 80, "/", false, "$invalid_name="); 
  -            fail("MalformedCookieException must have been thrown");
  -        }
  -        catch(MalformedCookieException e) {
  -            // Expected            
  -        }
  +    public void testCookieNameStartingWithDollarSign() throws Exception {
  +        Header setcookie = new Header("Set-Cookie", "$invalid_name=");
  +        CookieSpec cookiespec = new CookieSpecBase();
  +        Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, setcookie);
  +        assertNotNull(parsed);
  +        assertEquals(1, parsed.length);
       }
  +
   
       /**
        * Tests if malformatted expires attribute is parsed correctly.
  
  
  
  1.3       +33 -4     jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2109Spec.java
  
  Index: TestCookieRFC2109Spec.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2109Spec.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- TestCookieRFC2109Spec.java	25 Apr 2004 11:57:39 -0000	1.2
  +++ TestCookieRFC2109Spec.java	5 Jun 2004 16:49:20 -0000	1.3
  @@ -236,6 +236,35 @@
       }
   
       /**
  +     * Tests if cookie constructor rejects cookie name containing blanks.
  +     */
  +    public void testCookieNameWithBlanks() throws Exception {
  +        Header setcookie = new Header("Set-Cookie", "invalid name=");
  +        CookieSpec cookiespec = new RFC2109Spec();
  +        try {
  +            Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, setcookie);
  +            fail("MalformedCookieException exception should have been thrown");
  +        } catch (MalformedCookieException e) {
  +            // expected
  +        }
  +    }
  +
  +
  +    /**
  +     * Tests if cookie constructor rejects cookie name starting with $.
  +     */
  +    public void testCookieNameStartingWithDollarSign() throws Exception {
  +        Header setcookie = new Header("Set-Cookie", "$invalid_name=");
  +        CookieSpec cookiespec = new RFC2109Spec();
  +        try {
  +            Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, setcookie);
  +            fail("MalformedCookieException exception should have been thrown");
  +        } catch (MalformedCookieException e) {
  +            // expected
  +        }
  +    }
  +
  +    /**
        * Tests if default cookie validator rejects cookies originating from a host without domain
        * where domain attribute does not match the host of origin 
        */
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: cvs commit: jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/cookie TestCookieCompatibilitySpec.java TestCookieRFC2109Spec.java

Posted by Oleg Kalnichevski <ol...@bluewin.ch>.
Odi,
I have no idea why the commit diff got trashed so badly. CVS content
appears correct.

Oleg


On Mon, 2004-06-07 at 09:42, Ortwin Glück wrote:
> What's the problem with this diff? Why is it so bad?
> 
> olegk@apache.org wrote:
> 
> > olegk       2004/06/05 09:49:21
> > 
> >   Modified:    httpclient/src/java/org/apache/commons/httpclient
> >                         Cookie.java
> >                httpclient/src/java/org/apache/commons/httpclient/cookie
> >                         RFC2109Spec.java
> >                httpclient/src/test/org/apache/commons/httpclient/cookie
> >                         TestCookieCompatibilitySpec.java
> >                         TestCookieRFC2109Spec.java
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: commons-dev-help@jakarta.apache.org
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: cvs commit: jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/cookie TestCookieCompatibilitySpec.java TestCookieRFC2109Spec.java

Posted by Ortwin Glück <or...@nose.ch>.
What's the problem with this diff? Why is it so bad?

olegk@apache.org wrote:

> olegk       2004/06/05 09:49:21
> 
>   Modified:    httpclient/src/java/org/apache/commons/httpclient
>                         Cookie.java
>                httpclient/src/java/org/apache/commons/httpclient/cookie
>                         RFC2109Spec.java
>                httpclient/src/test/org/apache/commons/httpclient/cookie
>                         TestCookieCompatibilitySpec.java
>                         TestCookieRFC2109Spec.java

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org