/* * $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 $ * $Revision: 480424 $ * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ * * ==================================================================== * * 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. * ==================================================================== * * 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 java.util.StringTokenizer; import java.util.Date; import java.util.Locale; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.text.ParseException; import org.apache.commons.httpclient.HeaderElement; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.Cookie; /** * <P>Netscape cookie draft 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 NetscapeDraftSpec extends CookieSpecBase { /** Default constructor */ public NetscapeDraftSpec() { super(); } /** * Parses the Set-Cookie value into an array of <tt>Cookie</tt>s. * * <p>Syntax of the Set-Cookie HTTP Response Header:</p> * * <p>This is the format a CGI script would use to add to * the HTTP headers a new piece of data which is to be stored by * the client for later retrieval.</p> * * <PRE> * Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure * </PRE> * * <p>Please note that Netscape draft specification does not fully * conform to the HTTP header format. Netscape draft does not specify * whether multiple cookies may be sent in one header. Hence, comma * character may be present in unquoted cookie value or unquoted * parameter value.</p> * * @link http://wp.netscape.com/newsref/std/cookie_spec.html * * @param host the host from which the <tt>Set-Cookie</tt> value was * received * @param port the port from which the <tt>Set-Cookie</tt> value was * received * @param path the path from which the <tt>Set-Cookie</tt> value was * received * @param secure <tt>true</tt> when the <tt>Set-Cookie</tt> value was * received over secure conection * @param header the <tt>Set-Cookie</tt> received from the server * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie value * @throws MalformedCookieException if an exception occurs during parsing * * @since 3.0 */ public Cookie[] parse(String host, int port, String path, boolean secure, final String header) throws MalformedCookieException { LOG.trace("enter NetscapeDraftSpec.parse(String, port, path, boolean, Header)"); if (host == null) { throw new IllegalArgumentException("Host of origin may not be null"); } if (host.trim().equals("")) { throw new IllegalArgumentException("Host of origin may not be blank"); } if (port < 0) { throw new IllegalArgumentException("Invalid port: " + port); } if (path == null) { throw new IllegalArgumentException("Path of origin may not be null."); } if (header == null) { throw new IllegalArgumentException("Header may not be null."); } if (path.trim().equals("")) { path = PATH_DELIM; } host = host.toLowerCase(); String defaultPath = path; int lastSlashIndex = defaultPath.lastIndexOf(PATH_DELIM); if (lastSlashIndex >= 0) { if (lastSlashIndex == 0) { //Do not remove the very first slash lastSlashIndex = 1; } defaultPath = defaultPath.substring(0, lastSlashIndex); } HeaderElement headerelement = new HeaderElement(header.toCharArray()); Cookie cookie = new Cookie(host, headerelement.getName(), headerelement.getValue(), defaultPath, null, false); // cycle through the parameters NameValuePair[] parameters = headerelement.getParameters(); // could be null. In case only a header element and no parameters. if (parameters != null) { for (int j = 0; j < parameters.length; j++) { parseAttribute(parameters[j], cookie); } } return new Cookie[] {cookie}; } /** * Parse the cookie attribute and update the corresponsing {@link Cookie} * properties as defined by the Netscape draft specification * * @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("expires")) { if (paramValue == null) { throw new MalformedCookieException( "Missing value for expires attribute"); } try { DateFormat expiryFormat = new SimpleDateFormat( "EEE, dd-MMM-yyyy HH:mm:ss z", Locale.US); Date date = expiryFormat.parse(paramValue); cookie.setExpiryDate(date); } catch (ParseException e) { throw new MalformedCookieException("Invalid expires " + "attribute: " + e.getMessage()); } } else { super.parseAttribute(attribute, cookie); } } /** * Performs domain-match as described in the Netscape draft. * @param host The target host. * @param domain The cookie domain attribute. * @return true if the specified host matches the given domain. */ public boolean domainMatch(final String host, final String domain) { return host.endsWith(domain); } /** * Performs Netscape draft 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("enterNetscapeDraftCookieProcessor " + "RCF2109CookieProcessor.validate(Cookie)"); // Perform generic validation super.validate(host, port, path, secure, cookie); // Perform Netscape Cookie draft specific validation if (host.indexOf(".") >= 0) { int domainParts = new StringTokenizer(cookie.getDomain(), ".") .countTokens(); if (isSpecialDomain(cookie.getDomain())) { if (domainParts < 2) { throw new MalformedCookieException("Domain attribute \"" + cookie.getDomain() + "\" violates the Netscape cookie specification for " + "special domains"); } } else { if (domainParts < 3) { throw new MalformedCookieException("Domain attribute \"" + cookie.getDomain() + "\" violates the Netscape cookie specification"); } } } } /** * Checks if the given domain is in one of the seven special * top level domains defined by the Netscape cookie specification. * @param domain The domain. * @return True if the specified domain is "special" */ private static boolean isSpecialDomain(final String domain) { final String ucDomain = domain.toUpperCase(); if (ucDomain.endsWith(".COM") || ucDomain.endsWith(".EDU") || ucDomain.endsWith(".NET") || ucDomain.endsWith(".GOV") || ucDomain.endsWith(".MIL") || ucDomain.endsWith(".ORG") || ucDomain.endsWith(".INT")) { return true; } return false; } }