View Javadoc

    1   /*
    2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/cookie/NetscapeDraftSpec.java,v 1.11 2004/05/13 04:02:00 mbecke Exp $
    3    * $Revision: 480424 $
    4    * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Licensed to the Apache Software Foundation (ASF) under one or more
    9    *  contributor license agreements.  See the NOTICE file distributed with
    10   *  this work for additional information regarding copyright ownership.
    11   *  The ASF licenses this file to You under the Apache License, Version 2.0
    12   *  (the "License"); you may not use this file except in compliance with
    13   *  the License.  You may obtain a copy of the License at
    14   *
    15   *      http://www.apache.org/licenses/LICENSE-2.0
    16   *
    17   *  Unless required by applicable law or agreed to in writing, software
    18   *  distributed under the License is distributed on an "AS IS" BASIS,
    19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    20   *  See the License for the specific language governing permissions and
    21   *  limitations under the License.
    22   * ====================================================================
    23   *
    24   * This software consists of voluntary contributions made by many
    25   * individuals on behalf of the Apache Software Foundation.  For more
    26   * information on the Apache Software Foundation, please see
    27   * <http://www.apache.org/>.
    28   *
    29   */
    30
    31  package org.apache.commons.httpclient.cookie;
    32
    33  import java.util.StringTokenizer;
    34  import java.util.Date;
    35  import java.util.Locale;
    36  import java.text.DateFormat;
    37  import java.text.SimpleDateFormat;
    38  import java.text.ParseException;
    39
    40  import org.apache.commons.httpclient.HeaderElement;
    41  import org.apache.commons.httpclient.NameValuePair;
    42  import org.apache.commons.httpclient.Cookie;
    43
    44  /**
    45   * <P>Netscape cookie draft specific cookie management functions
    46   *
    47   * @author  B.C. Holmes
    48   * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
    49   * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
    50   * @author Rod Waldhoff
    51   * @author dIon Gillard
    52   * @author Sean C. Sullivan
    53   * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
    54   * @author Marc A. Saegesser
    55   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
    56   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
    57   *
    58   * @since 2.0
    59   */
    60
    61  public class NetscapeDraftSpec extends CookieSpecBase {
    62
    63      /** Default constructor */
    64      public NetscapeDraftSpec() {
    65          super();
    66      }
    67
    68      /**
    69        * Parses the Set-Cookie value into an array of <tt>Cookie</tt>s.
    70        *
    71        * <p>Syntax of the Set-Cookie HTTP Response Header:</p>
    72        *
    73        * <p>This is the format a CGI script would use to add to
    74        * the HTTP headers a new piece of data which is to be stored by
    75        * the client for later retrieval.</p>
    76        *
    77        * <PRE>
    78        *  Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
    79        * </PRE>
    80        *
    81        * <p>Please note that Netscape draft specification does not fully
    82        * conform to the HTTP header format. Netscape draft does not specify
    83        * whether multiple cookies may be sent in one header. Hence, comma
    84        * character may be present in unquoted cookie value or unquoted
    85        * parameter value.</p>
    86        *
    87        * @link http://wp.netscape.com/newsref/std/cookie_spec.html
    88        *
    89        * @param host the host from which the <tt>Set-Cookie</tt> value was
    90        * received
    91        * @param port the port from which the <tt>Set-Cookie</tt> value was
    92        * received
    93        * @param path the path from which the <tt>Set-Cookie</tt> value was
    94        * received
    95        * @param secure <tt>true</tt> when the <tt>Set-Cookie</tt> value was
    96        * received over secure conection
    97        * @param header the <tt>Set-Cookie</tt> received from the server
    98        * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie value
    99        * @throws MalformedCookieException if an exception occurs during parsing
    100       *
    101       * @since 3.0
    102       */
    103     public Cookie[] parse(String host, int port, String path,
    104         boolean secure, final String header)
    105         throws MalformedCookieException {
    106
    107         LOG.trace("enter NetscapeDraftSpec.parse(String, port, path, boolean, Header)");
    108
    109         if (host == null) {
    110             throw new IllegalArgumentException("Host of origin may not be null");
    111         }
    112         if (host.trim().equals("")) {
    113             throw new IllegalArgumentException("Host of origin may not be blank");
    114         }
    115         if (port < 0) {
    116             throw new IllegalArgumentException("Invalid port: " + port);
    117         }
    118         if (path == null) {
    119             throw new IllegalArgumentException("Path of origin may not be null.");
    120         }
    121         if (header == null) {
    122             throw new IllegalArgumentException("Header may not be null.");
    123         }
    124
    125         if (path.trim().equals("")) {
    126             path = PATH_DELIM;
    127         }
    128         host = host.toLowerCase();
    129
    130         String defaultPath = path;
    131         int lastSlashIndex = defaultPath.lastIndexOf(PATH_DELIM);
    132         if (lastSlashIndex >= 0) {
    133             if (lastSlashIndex == 0) {
    134                 //Do not remove the very first slash
    135                 lastSlashIndex = 1;
    136             }
    137             defaultPath = defaultPath.substring(0, lastSlashIndex);
    138         }
    139         HeaderElement headerelement = new HeaderElement(header.toCharArray());
    140         Cookie cookie = new Cookie(host,
    141                        headerelement.getName(),
    142                        headerelement.getValue(),
    143                        defaultPath,
    144                        null,
    145                        false);
    146         // cycle through the parameters
    147         NameValuePair[] parameters = headerelement.getParameters();
    148         // could be null. In case only a header element and no parameters.
    149         if (parameters != null) {
    150             for (int j = 0; j < parameters.length; j++) {
    151                 parseAttribute(parameters[j], cookie);
    152             }
    153         }
    154         return new Cookie[] {cookie};
    155     }
    156
    157
    158     /**
    159       * Parse the cookie attribute and update the corresponsing {@link Cookie}
    160       * properties as defined by the Netscape draft specification
    161       *
    162       * @param attribute {@link NameValuePair} cookie attribute from the
    163       * <tt>Set- Cookie</tt>
    164       * @param cookie {@link Cookie} to be updated
    165       * @throws MalformedCookieException if an exception occurs during parsing
    166       */
    167     public void parseAttribute(
    168         final NameValuePair attribute, final Cookie cookie)
    169         throws MalformedCookieException {
    170
    171         if (attribute == null) {
    172             throw new IllegalArgumentException("Attribute may not be null.");
    173         }
    174         if (cookie == null) {
    175             throw new IllegalArgumentException("Cookie may not be null.");
    176         }
    177         final String paramName = attribute.getName().toLowerCase();
    178         final String paramValue = attribute.getValue();
    179
    180         if (paramName.equals("expires")) {
    181
    182             if (paramValue == null) {
    183                 throw new MalformedCookieException(
    184                     "Missing value for expires attribute");
    185             }
    186             try {
    187                 DateFormat expiryFormat = new SimpleDateFormat(
    188                     "EEE, dd-MMM-yyyy HH:mm:ss z", Locale.US);
    189                 Date date = expiryFormat.parse(paramValue);
    190                 cookie.setExpiryDate(date);
    191             } catch (ParseException e) {
    192                 throw new MalformedCookieException("Invalid expires "
    193                     + "attribute: " + e.getMessage());
    194             }
    195         } else {
    196             super.parseAttribute(attribute, cookie);
    197         }
    198     }
    199
    200     /**
    201      * Performs domain-match as described in the Netscape draft.
    202      * @param host The target host.
    203      * @param domain The cookie domain attribute.
    204      * @return true if the specified host matches the given domain.
    205      */
    206     public boolean domainMatch(final String host, final String domain) {
    207         return host.endsWith(domain);
    208     }
    209
    210     /**
    211       * Performs Netscape draft compliant {@link Cookie} validation
    212       *
    213       * @param host the host from which the {@link Cookie} was received
    214       * @param port the port from which the {@link Cookie} was received
    215       * @param path the path from which the {@link Cookie} was received
    216       * @param secure <tt>true</tt> when the {@link Cookie} was received
    217       * using a secure connection
    218       * @param cookie The cookie to validate.
    219       * @throws MalformedCookieException if an exception occurs during
    220       * validation
    221       */
    222     public void validate(String host, int port, String path,
    223         boolean secure, final Cookie cookie)
    224         throws MalformedCookieException {
    225
    226         LOG.trace("enterNetscapeDraftCookieProcessor "
    227             + "RCF2109CookieProcessor.validate(Cookie)");
    228         // Perform generic validation
    229         super.validate(host, port, path, secure, cookie);
    230         // Perform Netscape Cookie draft specific validation
    231         if (host.indexOf(".") >= 0) {
    232             int domainParts = new StringTokenizer(cookie.getDomain(), ".")
    233                 .countTokens();
    234
    235             if (isSpecialDomain(cookie.getDomain())) {
    236                 if (domainParts < 2) {
    237                     throw new MalformedCookieException("Domain attribute \""
    238                         + cookie.getDomain()
    239                         + "\" violates the Netscape cookie specification for "
    240                         + "special domains");
    241                 }
    242             } else {
    243                 if (domainParts < 3) {
    244                     throw new MalformedCookieException("Domain attribute \""
    245                         + cookie.getDomain()
    246                         + "\" violates the Netscape cookie specification");
    247                 }
    248             }
    249         }
    250     }
    251
    252     /**
    253      * Checks if the given domain is in one of the seven special
    254      * top level domains defined by the Netscape cookie specification.
    255      * @param domain The domain.
    256      * @return True if the specified domain is "special"
    257      */
    258     private static boolean isSpecialDomain(final String domain) {
    259         final String ucDomain = domain.toUpperCase();
    260         if (ucDomain.endsWith(".COM")
    261            || ucDomain.endsWith(".EDU")
    262            || ucDomain.endsWith(".NET")
    263            || ucDomain.endsWith(".GOV")
    264            || ucDomain.endsWith(".MIL")
    265            || ucDomain.endsWith(".ORG")
    266            || ucDomain.endsWith(".INT")) {
    267             return true;
    268         }
    269         return false;
    270     }
    271 }