View Javadoc

1   /*
2    * $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 $
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 org.apache.commons.httpclient.NameValuePair;
33  import org.apache.commons.httpclient.Cookie;
34  
35  /***
36   * <p>RFC 2109 specific cookie management functions
37   *
38   * @author  B.C. Holmes
39   * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
40   * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
41   * @author Rod Waldhoff
42   * @author dIon Gillard
43   * @author Sean C. Sullivan
44   * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
45   * @author Marc A. Saegesser
46   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
47   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
48   * 
49   * @since 2.0 
50   */
51  
52  public class RFC2109Spec extends CookieSpecBase {
53  
54      /*** Default constructor */    
55      public RFC2109Spec() {
56          super();
57      }
58  
59  
60      /***
61        * Parse RFC 2109 specific cookie attribute and update the corresponsing
62        * {@link Cookie} properties.
63        *
64        * @param attribute {@link NameValuePair} cookie attribute from the
65        * <tt>Set- Cookie</tt>
66        * @param cookie {@link Cookie} to be updated
67        * @throws MalformedCookieException if an exception occurs during parsing
68        */
69      public void parseAttribute(
70          final NameValuePair attribute, final Cookie cookie)
71          throws MalformedCookieException {
72            
73          if (attribute == null) {
74              throw new IllegalArgumentException("Attribute may not be null.");
75          }
76          if (cookie == null) {
77              throw new IllegalArgumentException("Cookie may not be null.");
78          }
79          final String paramName = attribute.getName().toLowerCase();
80          final String paramValue = attribute.getValue();
81  
82          if (paramName.equals("path")) {
83              if (paramValue == null) {
84                  throw new MalformedCookieException(
85                      "Missing value for path attribute");
86              }
87              if (paramValue.trim().equals("")) {
88                  throw new MalformedCookieException(
89                      "Blank value for path attribute");
90              }
91              cookie.setPath(paramValue);
92              cookie.setPathAttributeSpecified(true);
93          } else if (paramName.equals("version")) {
94  
95              if (paramValue == null) {
96                  throw new MalformedCookieException(
97                      "Missing value for version attribute");
98              }
99              try {
100                cookie.setVersion(Integer.parseInt(paramValue));
101             } catch (NumberFormatException e) {
102                 throw new MalformedCookieException("Invalid version: " 
103                     + e.getMessage());
104             }
105 
106         } else {
107             super.parseAttribute(attribute, cookie);
108         }
109     }
110 
111     /***
112       * Performs RFC 2109 compliant {@link Cookie} validation
113       *
114       * @param host the host from which the {@link Cookie} was received
115       * @param port the port from which the {@link Cookie} was received
116       * @param path the path from which the {@link Cookie} was received
117       * @param secure <tt>true</tt> when the {@link Cookie} was received using a
118       * secure connection
119       * @param cookie The cookie to validate
120       * @throws MalformedCookieException if an exception occurs during
121       * validation
122       */
123     public void validate(String host, int port, String path, 
124         boolean secure, final Cookie cookie) throws MalformedCookieException {
125             
126         LOG.trace("enter RFC2109Spec.validate(String, int, String, "
127             + "boolean, Cookie)");
128             
129         // Perform generic validation
130         super.validate(host, port, path, secure, cookie);
131         // Perform RFC 2109 specific validation
132         
133         if (cookie.getName().indexOf(' ') != -1) {
134             throw new MalformedCookieException("Cookie name may not contain blanks");
135         }
136         if (cookie.getName().startsWith("$")) {
137             throw new MalformedCookieException("Cookie name may not start with $");
138         }
139         
140         if (cookie.isDomainAttributeSpecified() 
141             && (!cookie.getDomain().equals(host))) {
142                 
143             // domain must start with dot
144             if (!cookie.getDomain().startsWith(".")) {
145                 throw new MalformedCookieException("Domain attribute \"" 
146                     + cookie.getDomain() 
147                     + "\" violates RFC 2109: domain must start with a dot");
148             }
149             // domain must have at least one embedded dot
150             int dotIndex = cookie.getDomain().indexOf('.', 1);
151             if (dotIndex < 0 || dotIndex == cookie.getDomain().length() - 1) {
152                 throw new MalformedCookieException("Domain attribute \"" 
153                     + cookie.getDomain() 
154                     + "\" violates RFC 2109: domain must contain an embedded dot");
155             }
156             host = host.toLowerCase();
157             if (!host.endsWith(cookie.getDomain())) {
158                 throw new MalformedCookieException(
159                     "Illegal domain attribute \"" + cookie.getDomain() 
160                     + "\". Domain of origin: \"" + host + "\"");
161             }
162             // host minus domain may not contain any dots
163             String hostWithoutDomain = host.substring(0, host.length() 
164                 - cookie.getDomain().length());
165             if (hostWithoutDomain.indexOf('.') != -1) {
166                 throw new MalformedCookieException("Domain attribute \"" 
167                     + cookie.getDomain() 
168                     + "\" violates RFC 2109: host minus domain may not contain any dots");
169             }
170         }
171     }
172 
173     /***
174      * Performs domain-match as defined by the RFC2109.
175      * @param host The target host.
176      * @param domain The cookie domain attribute.
177      * @return true if the specified host matches the given domain.
178      * 
179      * @since 3.0
180      */
181     public boolean domainMatch(String host, String domain) {
182         boolean match = host.equals(domain) 
183             || (domain.startsWith(".") && host.endsWith(domain));
184 
185         return match;
186     }
187 
188     /***
189      * Return a name/value string suitable for sending in a <tt>"Cookie"</tt>
190      * header as defined in RFC 2109 for backward compatibility with cookie
191      * version 0
192      * @param name The name.
193      * @param value The value
194      * @param version The cookie version 
195      * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
196      */
197 
198     private String formatNameValuePair(
199         final String name, final String value, int version) {
200             
201         final StringBuffer buffer = new StringBuffer();
202         if (version < 1) {
203             buffer.append(name);
204             buffer.append("=");
205             if (value != null) {
206                 buffer.append(value);   
207             }
208         } else {
209             buffer.append(name);
210             buffer.append("=\"");
211             if (value != null) {
212                 buffer.append(value);
213             }
214             buffer.append("\"");
215         }
216         return buffer.toString(); 
217     }
218 
219     /***
220      * Return a string suitable for sending in a <tt>"Cookie"</tt> header 
221      * as defined in RFC 2109 for backward compatibility with cookie version 0
222      * @param cookie a {@link Cookie} to be formatted as string
223      * @param version The version to use.
224      * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
225      */
226     private String formatCookieAsVer(Cookie cookie, int version) {
227         LOG.trace("enter RFC2109Spec.formatCookieAsVer(Cookie)");
228         StringBuffer buf = new StringBuffer();
229         buf.append(formatNameValuePair(cookie.getName(), 
230             cookie.getValue(), version));
231         if (cookie.getDomain() != null 
232             && cookie.isDomainAttributeSpecified()) {
233                 
234             buf.append("; ");
235             buf.append(formatNameValuePair("$Domain", 
236                 cookie.getDomain(), version));
237         }
238         if (cookie.getPath() != null && cookie.isPathAttributeSpecified()) {
239             buf.append("; ");
240             buf.append(formatNameValuePair("$Path", cookie.getPath(), version));
241         }
242         return buf.toString();
243     }
244 
245 
246     /***
247      * Return a string suitable for sending in a <tt>"Cookie"</tt> header as
248      * defined in RFC 2109
249      * @param cookie a {@link Cookie} to be formatted as string
250      * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
251      */
252     public String formatCookie(Cookie cookie) {
253         LOG.trace("enter RFC2109Spec.formatCookie(Cookie)");
254         if (cookie == null) {
255             throw new IllegalArgumentException("Cookie may not be null");
256         }
257         int ver = cookie.getVersion();
258         StringBuffer buffer = new StringBuffer();
259         buffer.append(formatNameValuePair("$Version", 
260           Integer.toString(ver), ver));
261         buffer.append("; ");
262         buffer.append(formatCookieAsVer(cookie, ver));
263         return buffer.toString();
264     }
265 
266     /***
267      * Create a RFC 2109 compliant <tt>"Cookie"</tt> header value containing all
268      * {@link Cookie}s in <i>cookies</i> suitable for sending in a <tt>"Cookie"
269      * </tt> header
270      * @param cookies an array of {@link Cookie}s to be formatted
271      * @return a string suitable for sending in a Cookie header.
272      */
273     public String formatCookies(Cookie[] cookies) {
274         LOG.trace("enter RFC2109Spec.formatCookieHeader(Cookie[])");
275         int version = Integer.MAX_VALUE;
276         // Pick the lowerest common denominator
277         for (int i = 0; i < cookies.length; i++) {
278             Cookie cookie = cookies[i];
279             if (cookie.getVersion() < version) {
280                 version = cookie.getVersion();
281             }
282         }
283         final StringBuffer buffer = new StringBuffer();
284         buffer.append(formatNameValuePair("$Version", 
285             Integer.toString(version), version));
286         for (int i = 0; i < cookies.length; i++) {
287             buffer.append("; ");
288             buffer.append(formatCookieAsVer(cookies[i], version));
289         }
290         return buffer.toString();
291     }
292 
293 }