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