/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/cookie/RFC2109Spec.java,v 1.21 2004/06/05 16:49:20 olegk Exp $
* $Revision: 507134 $
* $Date: 2007-02-13 19:18:05 +0100 (Tue, 13 Feb 2007) $
*
* ====================================================================
*
* 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 org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.util.ParameterFormatter;
/**
* <p>RFC 2109 specific cookie management functions
*
* @author B.C. Holmes
* @author <a href="mailto:[email protected]">Park, Sung-Gu</a>
* @author <a href="mailto:[email protected]">Doug Sale</a>
* @author Rod Waldhoff
* @author dIon Gillard
* @author Sean C. Sullivan
* @author <a href="mailto:[email protected]">John Evans</a>
* @author Marc A. Saegesser
* @author <a href="mailto:[email protected]">Oleg Kalnichevski</a>
* @author <a href="mailto:[email protected]">Mike Bowler</a>
*
* @since 2.0
*/
public class RFC2109Spec extends CookieSpecBase {
private final ParameterFormatter formatter;
/**
* Cookie Response Header name for cookies processed
* by this spec.
*/
public final static String SET_COOKIE_KEY = "set-cookie";
/** Default constructor */
public RFC2109Spec() {
super();
this.formatter = new ParameterFormatter();
this.formatter.setAlwaysUseQuotes(true);
}
/**
* 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 buffer The string buffer to use for output
* @param param The parameter.
* @param version The cookie version
*/
private void formatParam(final StringBuffer buffer, final NameValuePair param, int version) {
if (version < 1) {
buffer.append(param.getName());
buffer.append("=");
if (param.getValue() != null) {
buffer.append(param.getValue());
}
} else {
this.formatter.format(buffer, param);
}
}
/**
* 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 buffer The string buffer to use for output
* @param cookie The {@link Cookie} to be formatted as string
* @param version The version to use.
*/
private void formatCookieAsVer(final StringBuffer buffer, final Cookie cookie, int version) {
String value = cookie.getValue();
if (value == null) {
value = "";
}
formatParam(buffer, new NameValuePair(cookie.getName(), value), version);
if ((cookie.getPath() != null) && cookie.isPathAttributeSpecified()) {
buffer.append("; ");
formatParam(buffer, new NameValuePair("$Path", cookie.getPath()), version);
}
if ((cookie.getDomain() != null)
&& cookie.isDomainAttributeSpecified()) {
buffer.append("; ");
formatParam(buffer, new NameValuePair("$Domain", cookie.getDomain()), version);
}
}
/**
* 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 version = cookie.getVersion();
StringBuffer buffer = new StringBuffer();
formatParam(buffer,
new NameValuePair("$Version", Integer.toString(version)),
version);
buffer.append("; ");
formatCookieAsVer(buffer, cookie, version);
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();
formatParam(buffer,
new NameValuePair("$Version", Integer.toString(version)),
version);
for (int i = 0; i < cookies.length; i++) {
buffer.append("; ");
formatCookieAsVer(buffer, cookies[i], version);
}
return buffer.toString();
}
}
|