001: /*
002: * $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 $
003: * $Revision: 480424 $
004: * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
005: *
006: * ====================================================================
007: *
008: * Licensed to the Apache Software Foundation (ASF) under one or more
009: * contributor license agreements. See the NOTICE file distributed with
010: * this work for additional information regarding copyright ownership.
011: * The ASF licenses this file to You under the Apache License, Version 2.0
012: * (the "License"); you may not use this file except in compliance with
013: * the License. You may obtain a copy of the License at
014: *
015: * http://www.apache.org/licenses/LICENSE-2.0
016: *
017: * Unless required by applicable law or agreed to in writing, software
018: * distributed under the License is distributed on an "AS IS" BASIS,
019: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020: * See the License for the specific language governing permissions and
021: * limitations under the License.
022: * ====================================================================
023: *
024: * This software consists of voluntary contributions made by many
025: * individuals on behalf of the Apache Software Foundation. For more
026: * information on the Apache Software Foundation, please see
027: * <http://www.apache.org/>.
028: *
029: */
030:
031: package org.apache.commons.httpclient.cookie;
032:
033: import java.util.StringTokenizer;
034: import java.util.Date;
035: import java.util.Locale;
036: import java.text.DateFormat;
037: import java.text.SimpleDateFormat;
038: import java.text.ParseException;
039:
040: import org.apache.commons.httpclient.HeaderElement;
041: import org.apache.commons.httpclient.NameValuePair;
042: import org.apache.commons.httpclient.Cookie;
043:
044: /**
045: * <P>Netscape cookie draft specific cookie management functions
046: *
047: * @author B.C. Holmes
048: * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
049: * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
050: * @author Rod Waldhoff
051: * @author dIon Gillard
052: * @author Sean C. Sullivan
053: * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
054: * @author Marc A. Saegesser
055: * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
056: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
057: *
058: * @since 2.0
059: */
060:
061: public class NetscapeDraftSpec extends CookieSpecBase {
062:
063: /** Default constructor */
064: public NetscapeDraftSpec() {
065: super ();
066: }
067:
068: /**
069: * Parses the Set-Cookie value into an array of <tt>Cookie</tt>s.
070: *
071: * <p>Syntax of the Set-Cookie HTTP Response Header:</p>
072: *
073: * <p>This is the format a CGI script would use to add to
074: * the HTTP headers a new piece of data which is to be stored by
075: * the client for later retrieval.</p>
076: *
077: * <PRE>
078: * Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
079: * </PRE>
080: *
081: * <p>Please note that Netscape draft specification does not fully
082: * conform to the HTTP header format. Netscape draft does not specify
083: * whether multiple cookies may be sent in one header. Hence, comma
084: * character may be present in unquoted cookie value or unquoted
085: * parameter value.</p>
086: *
087: * @link http://wp.netscape.com/newsref/std/cookie_spec.html
088: *
089: * @param host the host from which the <tt>Set-Cookie</tt> value was
090: * received
091: * @param port the port from which the <tt>Set-Cookie</tt> value was
092: * received
093: * @param path the path from which the <tt>Set-Cookie</tt> value was
094: * received
095: * @param secure <tt>true</tt> when the <tt>Set-Cookie</tt> value was
096: * received over secure conection
097: * @param header the <tt>Set-Cookie</tt> received from the server
098: * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie value
099: * @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
108: .trace("enter NetscapeDraftSpec.parse(String, port, path, boolean, Header)");
109:
110: if (host == null) {
111: throw new IllegalArgumentException(
112: "Host of origin may not be null");
113: }
114: if (host.trim().equals("")) {
115: throw new IllegalArgumentException(
116: "Host of origin may not be blank");
117: }
118: if (port < 0) {
119: throw new IllegalArgumentException("Invalid port: " + port);
120: }
121: if (path == null) {
122: throw new IllegalArgumentException(
123: "Path of origin may not be null.");
124: }
125: if (header == null) {
126: throw new IllegalArgumentException(
127: "Header may not be null.");
128: }
129:
130: if (path.trim().equals("")) {
131: path = PATH_DELIM;
132: }
133: host = host.toLowerCase();
134:
135: String defaultPath = path;
136: int lastSlashIndex = defaultPath.lastIndexOf(PATH_DELIM);
137: if (lastSlashIndex >= 0) {
138: if (lastSlashIndex == 0) {
139: //Do not remove the very first slash
140: lastSlashIndex = 1;
141: }
142: defaultPath = defaultPath.substring(0, lastSlashIndex);
143: }
144: HeaderElement headerelement = new HeaderElement(header
145: .toCharArray());
146: Cookie cookie = new Cookie(host, headerelement.getName(),
147: headerelement.getValue(), defaultPath, null, false);
148: // cycle through the parameters
149: NameValuePair[] parameters = headerelement.getParameters();
150: // could be null. In case only a header element and no parameters.
151: if (parameters != null) {
152: for (int j = 0; j < parameters.length; j++) {
153: parseAttribute(parameters[j], cookie);
154: }
155: }
156: return new Cookie[] { cookie };
157: }
158:
159: /**
160: * Parse the cookie attribute and update the corresponsing {@link Cookie}
161: * properties as defined by the Netscape draft specification
162: *
163: * @param attribute {@link NameValuePair} cookie attribute from the
164: * <tt>Set- Cookie</tt>
165: * @param cookie {@link Cookie} to be updated
166: * @throws MalformedCookieException if an exception occurs during parsing
167: */
168: public void parseAttribute(final NameValuePair attribute,
169: final Cookie cookie) throws MalformedCookieException {
170:
171: if (attribute == null) {
172: throw new IllegalArgumentException(
173: "Attribute may not be null.");
174: }
175: if (cookie == null) {
176: throw new IllegalArgumentException(
177: "Cookie may not be null.");
178: }
179: final String paramName = attribute.getName().toLowerCase();
180: final String paramValue = attribute.getValue();
181:
182: if (paramName.equals("expires")) {
183:
184: if (paramValue == null) {
185: throw new MalformedCookieException(
186: "Missing value for expires attribute");
187: }
188: try {
189: DateFormat expiryFormat = new SimpleDateFormat(
190: "EEE, dd-MMM-yyyy HH:mm:ss z", Locale.US);
191: Date date = expiryFormat.parse(paramValue);
192: cookie.setExpiryDate(date);
193: } catch (ParseException e) {
194: throw new MalformedCookieException("Invalid expires "
195: + "attribute: " + e.getMessage());
196: }
197: } else {
198: super .parseAttribute(attribute, cookie);
199: }
200: }
201:
202: /**
203: * Performs domain-match as described in the Netscape draft.
204: * @param host The target host.
205: * @param domain The cookie domain attribute.
206: * @return true if the specified host matches the given domain.
207: */
208: public boolean domainMatch(final String host, final String domain) {
209: return host.endsWith(domain);
210: }
211:
212: /**
213: * Performs Netscape draft compliant {@link Cookie} validation
214: *
215: * @param host the host from which the {@link Cookie} was received
216: * @param port the port from which the {@link Cookie} was received
217: * @param path the path from which the {@link Cookie} was received
218: * @param secure <tt>true</tt> when the {@link Cookie} was received
219: * using a secure connection
220: * @param cookie The cookie to validate.
221: * @throws MalformedCookieException if an exception occurs during
222: * validation
223: */
224: public void validate(String host, int port, String path,
225: boolean secure, final Cookie cookie)
226: throws MalformedCookieException {
227:
228: LOG.trace("enterNetscapeDraftCookieProcessor "
229: + "RCF2109CookieProcessor.validate(Cookie)");
230: // Perform generic validation
231: super .validate(host, port, path, secure, cookie);
232: // Perform Netscape Cookie draft specific validation
233: if (host.indexOf(".") >= 0) {
234: int domainParts = new StringTokenizer(cookie.getDomain(),
235: ".").countTokens();
236:
237: if (isSpecialDomain(cookie.getDomain())) {
238: if (domainParts < 2) {
239: throw new MalformedCookieException(
240: "Domain attribute \""
241: + cookie.getDomain()
242: + "\" violates the Netscape cookie specification for "
243: + "special domains");
244: }
245: } else {
246: if (domainParts < 3) {
247: throw new MalformedCookieException(
248: "Domain attribute \""
249: + cookie.getDomain()
250: + "\" violates the Netscape cookie specification");
251: }
252: }
253: }
254: }
255:
256: /**
257: * Checks if the given domain is in one of the seven special
258: * top level domains defined by the Netscape cookie specification.
259: * @param domain The domain.
260: * @return True if the specified domain is "special"
261: */
262: private static boolean isSpecialDomain(final String domain) {
263: final String ucDomain = domain.toUpperCase();
264: if (ucDomain.endsWith(".COM") || ucDomain.endsWith(".EDU")
265: || ucDomain.endsWith(".NET")
266: || ucDomain.endsWith(".GOV")
267: || ucDomain.endsWith(".MIL")
268: || ucDomain.endsWith(".ORG")
269: || ucDomain.endsWith(".INT")) {
270: return true;
271: }
272: return false;
273: }
274: }
|