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