KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > httpclient > cookie > RFC2965Spec


1 /*
2  * $HeadURL: https://svn.apache.org/repos/asf/jakarta/commons/proper/httpclient/trunk/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java $
3  * $Revision: 507134 $
4  * $Date: 2007-02-13 18:18:05 +0000 (Tue, 13 Feb 2007) $
5  *
6  * ====================================================================
7  *
8  * Licensed to the Apache Software Foundation (ASF) under one or more
9  * contributor license agreements. See the NOTICE file distributed with
10  * this work for additional information regarding copyright ownership.
11  * The ASF licenses this file to You under the Apache License, Version 2.0
12  * (the "License"); you may not use this file except in compliance with
13  * the License. You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  * ====================================================================
23  *
24  * This software consists of voluntary contributions made by many
25  * individuals on behalf of the Apache Software Foundation. For more
26  * information on the Apache Software Foundation, please see
27  * <http://www.apache.org/>.
28  *
29  */

30
31 package org.apache.commons.httpclient.cookie;
32
33 import java.util.ArrayList JavaDoc;
34 import java.util.Arrays JavaDoc;
35 import java.util.Comparator JavaDoc;
36 import java.util.Date JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.LinkedList JavaDoc;
40 import java.util.List JavaDoc;
41 import java.util.Map JavaDoc;
42 import java.util.StringTokenizer JavaDoc;
43
44 import org.apache.commons.httpclient.Cookie;
45 import org.apache.commons.httpclient.Header;
46 import org.apache.commons.httpclient.HeaderElement;
47 import org.apache.commons.httpclient.NameValuePair;
48 import org.apache.commons.httpclient.util.ParameterFormatter;
49
50 /**
51  * <p>RFC 2965 specific cookie management functions.</p>
52  *
53  * @author jain.samit@gmail.com (Samit Jain)
54  *
55  * @since 3.1
56  */

57 public class RFC2965Spec extends CookieSpecBase implements CookieVersionSupport {
58
59     private static final Comparator JavaDoc PATH_COMPOARATOR = new CookiePathComparator();
60     
61     /**
62     * Cookie Response Header name for cookies processed
63     * by this spec.
64     */

65     public final static String JavaDoc SET_COOKIE2_KEY = "set-cookie2";
66     
67     /**
68     * used for formatting RFC 2956 style cookies
69     */

70     private final ParameterFormatter formatter;
71      
72     /**
73      * Stores the list of attribute handlers
74      */

75     private final List JavaDoc attribHandlerList;
76     
77     /**
78     * Stores attribute name -> attribute handler mappings
79     */

80     private final Map JavaDoc attribHandlerMap;
81
82     /**
83      * Fallback cookie spec (RFC 2109)
84      */

85     private final CookieSpec rfc2109;
86     
87     /**
88      * Default constructor
89      * */

90     public RFC2965Spec() {
91         super();
92         this.formatter = new ParameterFormatter();
93         this.formatter.setAlwaysUseQuotes(true);
94         this.attribHandlerMap = new HashMap JavaDoc(10);
95         this.attribHandlerList = new ArrayList JavaDoc(10);
96         this.rfc2109 = new RFC2109Spec();
97         
98         registerAttribHandler(Cookie2.PATH, new Cookie2PathAttributeHandler());
99         registerAttribHandler(Cookie2.DOMAIN, new Cookie2DomainAttributeHandler());
100         registerAttribHandler(Cookie2.PORT, new Cookie2PortAttributeHandler());
101         registerAttribHandler(Cookie2.MAXAGE, new Cookie2MaxageAttributeHandler());
102         registerAttribHandler(Cookie2.SECURE, new CookieSecureAttributeHandler());
103         registerAttribHandler(Cookie2.COMMENT, new CookieCommentAttributeHandler());
104         registerAttribHandler(Cookie2.COMMENTURL, new CookieCommentUrlAttributeHandler());
105         registerAttribHandler(Cookie2.DISCARD, new CookieDiscardAttributeHandler());
106         registerAttribHandler(Cookie2.VERSION, new Cookie2VersionAttributeHandler());
107     }
108
109     protected void registerAttribHandler(
110             final String JavaDoc name, final CookieAttributeHandler handler) {
111         if (name == null) {
112             throw new IllegalArgumentException JavaDoc("Attribute name may not be null");
113         }
114         if (handler == null) {
115             throw new IllegalArgumentException JavaDoc("Attribute handler may not be null");
116         }
117         if (!this.attribHandlerList.contains(handler)) {
118             this.attribHandlerList.add(handler);
119         }
120         this.attribHandlerMap.put(name, handler);
121     }
122     
123     /**
124      * Finds an attribute handler {@link CookieAttributeHandler} for the
125      * given attribute. Returns <tt>null</tt> if no attribute handler is
126      * found for the specified attribute.
127      *
128      * @param name attribute name. e.g. Domain, Path, etc.
129      * @return an attribute handler or <tt>null</tt>
130      */

131     protected CookieAttributeHandler findAttribHandler(final String JavaDoc name) {
132         return (CookieAttributeHandler) this.attribHandlerMap.get(name);
133     }
134     
135     /**
136      * Gets attribute handler {@link CookieAttributeHandler} for the
137      * given attribute.
138      *
139      * @param name attribute name. e.g. Domain, Path, etc.
140      * @throws IllegalStateException if handler not found for the
141      * specified attribute.
142      */

143     protected CookieAttributeHandler getAttribHandler(final String JavaDoc name) {
144         CookieAttributeHandler handler = findAttribHandler(name);
145         if (handler == null) {
146             throw new IllegalStateException JavaDoc("Handler not registered for " +
147                                             name + " attribute.");
148         } else {
149             return handler;
150         }
151     }
152
153     protected Iterator JavaDoc getAttribHandlerIterator() {
154         return this.attribHandlerList.iterator();
155     }
156     
157     /**
158      * Parses the Set-Cookie2 value into an array of <tt>Cookie</tt>s.
159      *
160      * <P>The syntax for the Set-Cookie2 response header is:
161      *
162      * <PRE>
163      * set-cookie = "Set-Cookie2:" cookies
164      * cookies = 1#cookie
165      * cookie = NAME "=" VALUE * (";" cookie-av)
166      * NAME = attr
167      * VALUE = value
168      * cookie-av = "Comment" "=" value
169      * | "CommentURL" "=" <"> http_URL <">
170      * | "Discard"
171      * | "Domain" "=" value
172      * | "Max-Age" "=" value
173      * | "Path" "=" value
174      * | "Port" [ "=" <"> portlist <"> ]
175      * | "Secure"
176      * | "Version" "=" 1*DIGIT
177      * portlist = 1#portnum
178      * portnum = 1*DIGIT
179      * </PRE>
180      *
181      * @param host the host from which the <tt>Set-Cookie2</tt> value was
182      * received
183      * @param port the port from which the <tt>Set-Cookie2</tt> value was
184      * received
185      * @param path the path from which the <tt>Set-Cookie2</tt> value was
186      * received
187      * @param secure <tt>true</tt> when the <tt>Set-Cookie2</tt> value was
188      * received over secure conection
189      * @param header the <tt>Set-Cookie2</tt> <tt>Header</tt> received from the server
190      * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie2 value
191      * @throws MalformedCookieException if an exception occurs during parsing
192      */

193     public Cookie[] parse(
194             String JavaDoc host, int port, String JavaDoc path, boolean secure, final Header header)
195             throws MalformedCookieException {
196         LOG.trace("enter RFC2965.parse("
197                   + "String, int, String, boolean, Header)");
198
199         if (header == null) {
200             throw new IllegalArgumentException JavaDoc("Header may not be null.");
201         }
202         if (header.getName() == null) {
203             throw new IllegalArgumentException JavaDoc("Header name may not be null.");
204         }
205
206         if (header.getName().equalsIgnoreCase(SET_COOKIE2_KEY)) {
207             // parse cookie2 cookies
208
return parse(host, port, path, secure, header.getValue());
209         } else if (header.getName().equalsIgnoreCase(RFC2109Spec.SET_COOKIE_KEY)) {
210             // delegate parsing of old-style cookies to rfc2109Spec
211
return this.rfc2109.parse(host, port, path, secure, header.getValue());
212         } else {
213             throw new MalformedCookieException("Header name is not valid. " +
214                                                "RFC 2965 supports \"set-cookie\" " +
215                                                "and \"set-cookie2\" headers.");
216         }
217     }
218
219     /**
220      * @see #parse(String, int, String, boolean, org.apache.commons.httpclient.Header)
221      */

222     public Cookie[] parse(String JavaDoc host, int port, String JavaDoc path,
223                           boolean secure, final String JavaDoc header)
224             throws MalformedCookieException {
225         LOG.trace("enter RFC2965Spec.parse("
226                   + "String, int, String, boolean, String)");
227
228         // before we do anything, lets check validity of arguments
229
if (host == null) {
230             throw new IllegalArgumentException JavaDoc(
231                     "Host of origin may not be null");
232         }
233         if (host.trim().equals("")) {
234             throw new IllegalArgumentException JavaDoc(
235                     "Host of origin may not be blank");
236         }
237         if (port < 0) {
238             throw new IllegalArgumentException JavaDoc("Invalid port: " + port);
239         }
240         if (path == null) {
241             throw new IllegalArgumentException JavaDoc(
242                     "Path of origin may not be null.");
243         }
244         if (header == null) {
245             throw new IllegalArgumentException JavaDoc("Header may not be null.");
246         }
247
248         if (path.trim().equals("")) {
249             path = PATH_DELIM;
250         }
251         host = getEffectiveHost(host);
252
253         HeaderElement[] headerElements =
254                 HeaderElement.parseElements(header.toCharArray());
255
256         List JavaDoc cookies = new LinkedList JavaDoc();
257         for (int i = 0; i < headerElements.length; i++) {
258             HeaderElement headerelement = headerElements[i];
259             Cookie2 cookie = null;
260             try {
261                 cookie = new Cookie2(host,
262                                     headerelement.getName(),
263                                     headerelement.getValue(),
264                                     path,
265                                     null,
266                                     false,
267                                     new int[] {port});
268             } catch (IllegalArgumentException JavaDoc ex) {
269                 throw new MalformedCookieException(ex.getMessage());
270             }
271             NameValuePair[] parameters = headerelement.getParameters();
272             // could be null. In case only a header element and no parameters.
273
if (parameters != null) {
274                 // Eliminate duplicate attribues. The first occurence takes precedence
275
Map JavaDoc attribmap = new HashMap JavaDoc(parameters.length);
276                 for (int j = parameters.length - 1; j >= 0; j--) {
277                     NameValuePair param = parameters[j];
278                     attribmap.put(param.getName().toLowerCase(), param);
279                 }
280                 for (Iterator JavaDoc it = attribmap.entrySet().iterator(); it.hasNext(); ) {
281                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
282                     parseAttribute((NameValuePair) entry.getValue(), cookie);
283                 }
284             }
285             cookies.add(cookie);
286             // cycle through the parameters
287
}
288         return (Cookie[]) cookies.toArray(new Cookie[cookies.size()]);
289     }
290
291     /**
292      * Parse RFC 2965 specific cookie attribute and update the corresponsing
293      * {@link org.apache.commons.httpclient.Cookie} properties.
294      *
295      * @param attribute {@link org.apache.commons.httpclient.NameValuePair} cookie attribute from the
296      * <tt>Set-Cookie2</tt> header.
297      * @param cookie {@link org.apache.commons.httpclient.Cookie} to be updated
298      * @throws MalformedCookieException if an exception occurs during parsing
299      */

300     public void parseAttribute(
301             final NameValuePair attribute, final Cookie cookie)
302             throws MalformedCookieException {
303         if (attribute == null) {
304             throw new IllegalArgumentException JavaDoc("Attribute may not be null.");
305         }
306         if (attribute.getName() == null) {
307             throw new IllegalArgumentException JavaDoc("Attribute Name may not be null.");
308         }
309         if (cookie == null) {
310             throw new IllegalArgumentException JavaDoc("Cookie may not be null.");
311         }
312         final String JavaDoc paramName = attribute.getName().toLowerCase();
313         final String JavaDoc paramValue = attribute.getValue();
314
315         CookieAttributeHandler handler = findAttribHandler(paramName);
316         if (handler == null) {
317             // ignore unknown attribute-value pairs
318
if (LOG.isDebugEnabled())
319                 LOG.debug("Unrecognized cookie attribute: " +
320                           attribute.toString());
321         } else {
322             handler.parse(cookie, paramValue);
323         }
324     }
325
326     /**
327      * Performs RFC 2965 compliant {@link org.apache.commons.httpclient.Cookie} validation
328      *
329      * @param host the host from which the {@link org.apache.commons.httpclient.Cookie} was received
330      * @param port the port from which the {@link org.apache.commons.httpclient.Cookie} was received
331      * @param path the path from which the {@link org.apache.commons.httpclient.Cookie} was received
332      * @param secure <tt>true</tt> when the {@link org.apache.commons.httpclient.Cookie} was received using a
333      * secure connection
334      * @param cookie The cookie to validate
335      * @throws MalformedCookieException if an exception occurs during
336      * validation
337      */

338     public void validate(final String JavaDoc host, int port, final String JavaDoc path,
339                          boolean secure, final Cookie cookie)
340             throws MalformedCookieException {
341
342         LOG.trace("enter RFC2965Spec.validate(String, int, String, "
343                   + "boolean, Cookie)");
344
345         if (cookie instanceof Cookie2) {
346             if (cookie.getName().indexOf(' ') != -1) {
347                 throw new MalformedCookieException("Cookie name may not contain blanks");
348             }
349             if (cookie.getName().startsWith("$")) {
350                 throw new MalformedCookieException("Cookie name may not start with $");
351             }
352             CookieOrigin origin = new CookieOrigin(getEffectiveHost(host), port, path, secure);
353             for (Iterator JavaDoc i = getAttribHandlerIterator(); i.hasNext(); ) {
354               CookieAttributeHandler handler = (CookieAttributeHandler) i.next();
355               handler.validate(cookie, origin);
356             }
357         } else {
358             // old-style cookies are validated according to the old rules
359
this.rfc2109.validate(host, port, path, secure, cookie);
360         }
361     }
362
363     /**
364      * Return <tt>true</tt> if the cookie should be submitted with a request
365      * with given attributes, <tt>false</tt> otherwise.
366      * @param host the host to which the request is being submitted
367      * @param port the port to which the request is being submitted (ignored)
368      * @param path the path to which the request is being submitted
369      * @param secure <tt>true</tt> if the request is using a secure connection
370      * @return true if the cookie matches the criterium
371      */

372     public boolean match(String JavaDoc host, int port, String JavaDoc path,
373                          boolean secure, final Cookie cookie) {
374
375         LOG.trace("enter RFC2965.match("
376                   + "String, int, String, boolean, Cookie");
377         if (cookie == null) {
378             throw new IllegalArgumentException JavaDoc("Cookie may not be null");
379         }
380         if (cookie instanceof Cookie2) {
381             // check if cookie has expired
382
if (cookie.isPersistent() && cookie.isExpired()) {
383                 return false;
384             }
385             CookieOrigin origin = new CookieOrigin(getEffectiveHost(host), port, path, secure);
386             for (Iterator JavaDoc i = getAttribHandlerIterator(); i.hasNext(); ) {
387                 CookieAttributeHandler handler = (CookieAttributeHandler) i.next();
388                 if (!handler.match(cookie, origin)) {
389                     return false;
390                 }
391             }
392             return true;
393         } else {
394             // old-style cookies are matched according to the old rules
395
return this.rfc2109.match(host, port, path, secure, cookie);
396         }
397     }
398
399     private void doFormatCookie2(final Cookie2 cookie, final StringBuffer JavaDoc buffer) {
400         String JavaDoc name = cookie.getName();
401         String JavaDoc value = cookie.getValue();
402         if (value == null) {
403             value = "";
404         }
405         this.formatter.format(buffer, new NameValuePair(name, value));
406         // format domain attribute
407
if (cookie.getDomain() != null && cookie.isDomainAttributeSpecified()) {
408             buffer.append("; ");
409             this.formatter.format(buffer, new NameValuePair("$Domain", cookie.getDomain()));
410         }
411         // format path attribute
412
if ((cookie.getPath() != null) && (cookie.isPathAttributeSpecified())) {
413             buffer.append("; ");
414             this.formatter.format(buffer, new NameValuePair("$Path", cookie.getPath()));
415         }
416         // format port attribute
417
if (cookie.isPortAttributeSpecified()) {
418             String JavaDoc portValue = "";
419             if (!cookie.isPortAttributeBlank()) {
420                 portValue = createPortAttribute(cookie.getPorts());
421             }
422             buffer.append("; ");
423             this.formatter.format(buffer, new NameValuePair("$Port", portValue));
424         }
425     }
426     
427     /**
428      * Return a string suitable for sending in a <tt>"Cookie"</tt> header as
429      * defined in RFC 2965
430      * @param cookie a {@link org.apache.commons.httpclient.Cookie} to be formatted as string
431      * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
432      */

433     public String JavaDoc formatCookie(final Cookie cookie) {
434         LOG.trace("enter RFC2965Spec.formatCookie(Cookie)");
435
436         if (cookie == null) {
437             throw new IllegalArgumentException JavaDoc("Cookie may not be null");
438         }
439         if (cookie instanceof Cookie2) {
440             Cookie2 cookie2 = (Cookie2) cookie;
441             int version = cookie2.getVersion();
442             final StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
443             this.formatter.format(buffer, new NameValuePair("$Version", Integer.toString(version)));
444             buffer.append("; ");
445             doFormatCookie2(cookie2, buffer);
446             return buffer.toString();
447         } else {
448             // old-style cookies are formatted according to the old rules
449
return this.rfc2109.formatCookie(cookie);
450         }
451     }
452
453     /**
454      * Create a RFC 2965 compliant <tt>"Cookie"</tt> header value containing all
455      * {@link org.apache.commons.httpclient.Cookie}s suitable for
456      * sending in a <tt>"Cookie"</tt> header
457      * @param cookies an array of {@link org.apache.commons.httpclient.Cookie}s to be formatted
458      * @return a string suitable for sending in a Cookie header.
459      */

460     public String JavaDoc formatCookies(final Cookie[] cookies) {
461         LOG.trace("enter RFC2965Spec.formatCookieHeader(Cookie[])");
462
463         if (cookies == null) {
464             throw new IllegalArgumentException JavaDoc("Cookies may not be null");
465         }
466         // check if cookies array contains a set-cookie (old style) cookie
467
boolean hasOldStyleCookie = false;
468         int version = -1;
469         for (int i = 0; i < cookies.length; i++) {
470             Cookie cookie = cookies[i];
471             if (!(cookie instanceof Cookie2)) {
472                 hasOldStyleCookie = true;
473                 break;
474             }
475             if (cookie.getVersion() > version) {
476                 version = cookie.getVersion();
477             }
478         }
479         if (version < 0) {
480             version = 0;
481         }
482         if (hasOldStyleCookie || version < 1) {
483             // delegate old-style cookie formatting to rfc2109Spec
484
return this.rfc2109.formatCookies(cookies);
485         }
486         // Arrange cookies by path
487
Arrays.sort(cookies, PATH_COMPOARATOR);
488         
489         final StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
490         // format cookie version
491
this.formatter.format(buffer, new NameValuePair("$Version", Integer.toString(version)));
492         for (int i = 0; i < cookies.length; i++) {
493             buffer.append("; ");
494             Cookie2 cookie = (Cookie2) cookies[i];
495             // format cookie attributes
496
doFormatCookie2(cookie, buffer);
497         }
498         return buffer.toString();
499     }
500
501     /**
502      * Retrieves valid Port attribute value for the given ports array.
503      * e.g. "8000,8001,8002"
504      *
505      * @param ports int array of ports
506      */

507     private String JavaDoc createPortAttribute(int[] ports) {
508         StringBuffer JavaDoc portValue = new StringBuffer JavaDoc();
509         for (int i = 0, len = ports.length; i < len; i++) {
510             if (i > 0) {
511                 portValue.append(",");
512             }
513             portValue.append(ports[i]);
514         }
515         return portValue.toString();
516     }
517
518     /**
519      * Parses the given Port attribute value (e.g. "8000,8001,8002")
520      * into an array of ports.
521      *
522      * @param portValue port attribute value
523      * @return parsed array of ports
524      * @throws MalformedCookieException if there is a problem in
525      * parsing due to invalid portValue.
526      */

527     private int[] parsePortAttribute(final String JavaDoc portValue)
528             throws MalformedCookieException {
529         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(portValue, ",");
530         int[] ports = new int[st.countTokens()];
531         try {
532             int i = 0;
533             while(st.hasMoreTokens()) {
534                 ports[i] = Integer.parseInt(st.nextToken().trim());
535                 if (ports[i] < 0) {
536                   throw new MalformedCookieException ("Invalid Port attribute.");
537                 }
538                 ++i;
539             }
540         } catch (NumberFormatException JavaDoc e) {
541             throw new MalformedCookieException ("Invalid Port "
542                                                 + "attribute: " + e.getMessage());
543         }
544         return ports;
545     }
546
547     /**
548      * Gets 'effective host name' as defined in RFC 2965.
549      * <p>
550      * If a host name contains no dots, the effective host name is
551      * that name with the string .local appended to it. Otherwise
552      * the effective host name is the same as the host name. Note
553      * that all effective host names contain at least one dot.
554      *
555      * @param host host name where cookie is received from or being sent to.
556      * @return
557      */

558     private static String JavaDoc getEffectiveHost(final String JavaDoc host) {
559         String JavaDoc effectiveHost = host.toLowerCase();
560         if (host.indexOf('.') < 0) {
561             effectiveHost += ".local";
562         }
563         return effectiveHost;
564     }
565
566     /**
567      * Performs domain-match as defined by the RFC2965.
568      * <p>
569      * Host A's name domain-matches host B's if
570      * <ol>
571      * <ul>their host name strings string-compare equal; or</ul>
572      * <ul>A is a HDN string and has the form NB, where N is a non-empty
573      * name string, B has the form .B', and B' is a HDN string. (So,
574      * x.y.com domain-matches .Y.com but not Y.com.)</ul>
575      * </ol>
576      *
577      * @param host host name where cookie is received from or being sent to.
578      * @param domain The cookie domain attribute.
579      * @return true if the specified host matches the given domain.
580      */

581     public boolean domainMatch(String JavaDoc host, String JavaDoc domain) {
582         boolean match = host.equals(domain)
583                         || (domain.startsWith(".") && host.endsWith(domain));
584
585         return match;
586     }
587
588     /**
589      * Returns <tt>true</tt> if the given port exists in the given
590      * ports list.
591      *
592      * @param port port of host where cookie was received from or being sent to.
593      * @param ports port list
594      * @return true returns <tt>true</tt> if the given port exists in
595      * the given ports list; <tt>false</tt> otherwise.
596      */

597     private boolean portMatch(int port, int[] ports) {
598         boolean portInList = false;
599         for (int i = 0, len = ports.length; i < len; i++) {
600             if (port == ports[i]) {
601                 portInList = true;
602                 break;
603             }
604         }
605         return portInList;
606     }
607
608     /**
609      * <tt>"Path"</tt> attribute handler for RFC 2965 cookie spec.
610      */

611     private class Cookie2PathAttributeHandler
612             implements CookieAttributeHandler {
613
614         /**
615          * Parse cookie path attribute.
616          */

617         public void parse(final Cookie cookie, final String JavaDoc path)
618                 throws MalformedCookieException {
619             if (cookie == null) {
620                 throw new IllegalArgumentException JavaDoc("Cookie may not be null");
621             }
622             if (path == null) {
623                 throw new MalformedCookieException(
624                         "Missing value for path attribute");
625             }
626             if (path.trim().equals("")) {
627                 throw new MalformedCookieException(
628                         "Blank value for path attribute");
629             }
630             cookie.setPath(path);
631             cookie.setPathAttributeSpecified(true);
632         }
633
634         /**
635          * Validate cookie path attribute. The value for the Path attribute must be a
636          * prefix of the request-URI (case-sensitive matching).
637          */

638         public void validate(final Cookie cookie, final CookieOrigin origin)
639                 throws MalformedCookieException {
640             if (cookie == null) {
641                 throw new IllegalArgumentException JavaDoc("Cookie may not be null");
642             }
643             if (origin == null) {
644                 throw new IllegalArgumentException JavaDoc("Cookie origin may not be null");
645             }
646             String JavaDoc path = origin.getPath();
647             if (path == null) {
648                 throw new IllegalArgumentException JavaDoc(
649                         "Path of origin host may not be null.");
650             }
651             if (cookie.getPath() == null) {
652                 throw new MalformedCookieException("Invalid cookie state: " +
653                                                    "path attribute is null.");
654             }
655             if (path.trim().equals("")) {
656                 path = PATH_DELIM;
657             }
658
659             if (!pathMatch(path, cookie.getPath())) {
660                 throw new MalformedCookieException(
661                         "Illegal path attribute \"" + cookie.getPath()
662                         + "\". Path of origin: \"" + path + "\"");
663             }
664         }
665
666         /**
667          * Match cookie path attribute. The value for the Path attribute must be a
668          * prefix of the request-URI (case-sensitive matching).
669          */

670         public boolean match(final Cookie cookie, final CookieOrigin origin) {
671             if (cookie == null) {
672                 throw new IllegalArgumentException JavaDoc("Cookie may not be null");
673             }
674             if (origin == null) {
675                 throw new IllegalArgumentException JavaDoc("Cookie origin may not be null");
676             }
677             String JavaDoc path = origin.getPath();
678             if (cookie.getPath() == null) {
679                 LOG.warn("Invalid cookie state: path attribute is null.");
680                 return false;
681             }
682             if (path.trim().equals("")) {
683                 path = PATH_DELIM;
684             }
685
686             if (!pathMatch(path, cookie.getPath())) {
687                 return false;
688             }
689             return true;
690         }
691     }
692
693     /**
694      * <tt>"Domain"</tt> cookie attribute handler for RFC 2965 cookie spec.
695      */

696     private class Cookie2DomainAttributeHandler
697             implements CookieAttributeHandler {
698
699         /**
700          * Parse cookie domain attribute.
701          */

702         public void parse(final Cookie cookie, String JavaDoc domain)
703                 throws MalformedCookieException {
704             if (cookie == null) {
705                 throw new IllegalArgumentException JavaDoc("Cookie may not be null");
706             }
707             if (domain == null) {
708                 throw new MalformedCookieException(
709                         "Missing value for domain attribute");
710             }
711             if (domain.trim().equals("")) {
712                 throw new MalformedCookieException(
713                         "Blank value for domain attribute");
714             }
715             domain = domain.toLowerCase();
716             if (!domain.startsWith(".")) {
717                 // Per RFC 2965 section 3.2.2
718
// "... If an explicitly specified value does not start with
719
// a dot, the user agent supplies a leading dot ..."
720
// That effectively implies that the domain attribute
721
// MAY NOT be an IP address of a host name
722
domain = "." + domain;
723             }
724             cookie.setDomain(domain);
725             cookie.setDomainAttributeSpecified(true);
726         }
727
728         /**
729          * Validate cookie domain attribute.
730          */

731         public void validate(final Cookie cookie, final CookieOrigin origin)
732                 throws MalformedCookieException {
733             if (cookie == null) {
734                 throw new IllegalArgumentException JavaDoc("Cookie may not be null");
735             }
736             if (origin == null) {
737                 throw new IllegalArgumentException JavaDoc("Cookie origin may not be null");
738             }
739             String JavaDoc host = origin.getHost().toLowerCase();
740             if (cookie.getDomain() == null) {
741                 throw new MalformedCookieException("Invalid cookie state: " +
742                                                    "domain not specified");
743             }
744             String JavaDoc cookieDomain = cookie.getDomain().toLowerCase();
745
746             if (cookie.isDomainAttributeSpecified()) {
747                 // Domain attribute must start with a dot
748
if (!cookieDomain.startsWith(".")) {
749                     throw new MalformedCookieException("Domain attribute \"" +
750                         cookie.getDomain() + "\" violates RFC 2109: domain must start with a dot");
751                 }
752
753                 // Domain attribute must contain atleast one embedded dot,
754
// or the value must be equal to .local.
755
int dotIndex = cookieDomain.indexOf('.', 1);
756                 if (((dotIndex < 0) || (dotIndex == cookieDomain.length() - 1))
757                     && (!cookieDomain.equals(".local"))) {
758                     throw new MalformedCookieException(
759                             "Domain attribute \"" + cookie.getDomain()
760                             + "\" violates RFC 2965: the value contains no embedded dots "
761                             + "and the value is not .local");
762                 }
763
764                 // The effective host name must domain-match domain attribute.
765
if (!domainMatch(host, cookieDomain)) {
766                     throw new MalformedCookieException(
767                             "Domain attribute \"" + cookie.getDomain()
768                             + "\" violates RFC 2965: effective host name does not "
769                             + "domain-match domain attribute.");
770                 }
771
772                 // effective host name minus domain must not contain any dots
773
String JavaDoc effectiveHostWithoutDomain = host.substring(
774                         0, host.length() - cookieDomain.length());
775                 if (effectiveHostWithoutDomain.indexOf('.') != -1) {
776                     throw new MalformedCookieException("Domain attribute \""
777                                                        + cookie.getDomain() + "\" violates RFC 2965: "
778                                                        + "effective host minus domain may not contain any dots");
779                 }
780             } else {
781                 // Domain was not specified in header. In this case, domain must
782
// string match request host (case-insensitive).
783
if (!cookie.getDomain().equals(host)) {
784                     throw new MalformedCookieException("Illegal domain attribute: \""
785                                                        + cookie.getDomain() + "\"."
786                                                        + "Domain of origin: \""
787                                                        + host + "\"");
788                 }
789             }
790         }
791
792         /**
793          * Match cookie domain attribute.
794          */

795         public boolean match(final Cookie cookie, final CookieOrigin origin) {
796             if (cookie == null) {
797                 throw new IllegalArgumentException JavaDoc("Cookie may not be null");
798             }
799             if (origin == null) {
800                 throw new IllegalArgumentException JavaDoc("Cookie origin may not be null");
801             }
802             String JavaDoc host = origin.getHost().toLowerCase();
803             String JavaDoc cookieDomain = cookie.getDomain();
804
805             // The effective host name MUST domain-match the Domain
806
// attribute of the cookie.
807
if (!domainMatch(host, cookieDomain)) {
808                 return false;
809             }
810             // effective host name minus domain must not contain any dots
811
String JavaDoc effectiveHostWithoutDomain = host.substring(
812                     0, host.length() - cookieDomain.length());
813             if (effectiveHostWithoutDomain.indexOf('.') != -1) {
814                 return false;
815             }
816             return true;
817         }
818
819     }
820
821     /**
822      * <tt>"Port"</tt> cookie attribute handler for RFC 2965 cookie spec.
823      */

824     private class Cookie2PortAttributeHandler
825             implements CookieAttributeHandler {
826
827         /**
828          * Parse cookie port attribute.
829          */

830         public void parse(final Cookie cookie, final String JavaDoc portValue)
831                 throws MalformedCookieException {
832             if (cookie == null) {
833                 throw new IllegalArgumentException JavaDoc("Cookie may not be null");
834             }
835             if (cookie instanceof Cookie2) {
836                 Cookie2 cookie2 = (Cookie2) cookie;
837                 if ((portValue == null) || (portValue.trim().equals(""))) {
838                     // If the Port attribute is present but has no value, the
839
// cookie can only be sent to the request-port.
840
// Since the default port list contains only request-port, we don't
841
// need to do anything here.
842
cookie2.setPortAttributeBlank(true);
843                 } else {
844                     int[] ports = parsePortAttribute(portValue);
845                     cookie2.setPorts(ports);
846                 }
847                 cookie2.setPortAttributeSpecified(true);
848             }
849         }
850
851         /**
852          * Validate cookie port attribute. If the Port attribute was specified
853          * in header, the request port must be in cookie's port list.
854          */

855         public void validate(final Cookie cookie, final CookieOrigin origin)
856                 throws MalformedCookieException {
857             if (cookie == null) {
858                 throw new IllegalArgumentException JavaDoc("Cookie may not be null");
859             }
860             if (origin == null) {
861                 throw new IllegalArgumentException JavaDoc("Cookie origin may not be null");
862             }
863             if (cookie instanceof Cookie2) {
864                 Cookie2 cookie2 = (Cookie2) cookie;
865                 int port = origin.getPort();
866                 if (cookie2.isPortAttributeSpecified()) {
867                     if (!portMatch(port, cookie2.getPorts())) {
868                         throw new MalformedCookieException(
869                                 "Port attribute violates RFC 2965: "
870                                 + "Request port not found in cookie's port list.");
871                     }
872                 }
873             }
874         }
875
876         /**
877          * Match cookie port attribute. If the Port attribute is not specified
878          * in header, the cookie can be sent to any port. Otherwise, the request port
879          * must be in the cookie's port list.
880          */

881         public boolean match(final Cookie cookie, final CookieOrigin origin) {
882             if (cookie == null) {
883                 throw new IllegalArgumentException JavaDoc("Cookie may not be null");
884             }
885             if (origin == null) {
886                 throw new IllegalArgumentException JavaDoc("Cookie origin may not be null");
887             }
888             if (cookie instanceof Cookie2) {
889                 Cookie2 cookie2 = (Cookie2) cookie;
890                 int port = origin.getPort();
891                 if (cookie2.isPortAttributeSpecified()) {
892                     if (cookie2.getPorts() == null) {
893                         LOG.warn("Invalid cookie state: port not specified");
894                         return false;
895                     }
896                     if (!portMatch(port, cookie2.getPorts())) {
897                         return false;
898                     }
899                 }
900                 return true;
901             } else {
902                 return false;
903             }
904         }
905     }
906
907   /**
908    * <tt>"Max-age"</tt> cookie attribute handler for RFC 2965 cookie spec.
909    */

910   private class Cookie2MaxageAttributeHandler
911           implements CookieAttributeHandler {
912
913       /**
914        * Parse cookie max-age attribute.
915        */

916       public void parse(final Cookie cookie, final String JavaDoc value)
917               throws MalformedCookieException {
918           if (cookie == null) {
919               throw new IllegalArgumentException JavaDoc("Cookie may not be null");
920           }
921           if (value == null) {
922               throw new MalformedCookieException(
923                       "Missing value for max-age attribute");
924           }
925           int age = -1;
926           try {
927               age = Integer.parseInt(value);
928           } catch (NumberFormatException JavaDoc e) {
929               age = -1;
930           }
931           if (age < 0) {
932               throw new MalformedCookieException ("Invalid max-age attribute.");
933           }
934           cookie.setExpiryDate(new Date JavaDoc(System.currentTimeMillis() + age * 1000L));
935       }
936
937       /**
938        * validate cookie max-age attribute.
939        */

940       public void validate(final Cookie cookie, final CookieOrigin origin) {
941       }
942
943       /**
944        * @see CookieAttributeHandler#match(org.apache.commons.httpclient.Cookie, String)
945        */

946       public boolean match(final Cookie cookie, final CookieOrigin origin) {
947           return true;
948       }
949
950   }
951
952   /**
953    * <tt>"Secure"</tt> cookie attribute handler for RFC 2965 cookie spec.
954    */

955   private class CookieSecureAttributeHandler
956           implements CookieAttributeHandler {
957
958       public void parse(final Cookie cookie, final String JavaDoc secure)
959               throws MalformedCookieException {
960           cookie.setSecure(true);
961       }
962
963       public void validate(final Cookie cookie, final CookieOrigin origin)
964               throws MalformedCookieException {
965       }
966
967       public boolean match(final Cookie cookie, final CookieOrigin origin) {
968           if (cookie == null) {
969               throw new IllegalArgumentException JavaDoc("Cookie may not be null");
970           }
971           if (origin == null) {
972               throw new IllegalArgumentException JavaDoc("Cookie origin may not be null");
973           }
974           return cookie.getSecure() == origin.isSecure();
975       }
976       
977   }
978
979   /**
980    * <tt>"Commant"</tt> cookie attribute handler for RFC 2965 cookie spec.
981    */

982   private class CookieCommentAttributeHandler
983           implements CookieAttributeHandler {
984
985       public void parse(final Cookie cookie, final String JavaDoc comment)
986               throws MalformedCookieException {
987           cookie.setComment(comment);
988       }
989
990       public void validate(final Cookie cookie, final CookieOrigin origin)
991               throws MalformedCookieException {
992       }
993
994       public boolean match(final Cookie cookie, final CookieOrigin origin) {
995           return true;
996       }
997       
998   }
999
1000  /**
1001   * <tt>"CommantURL"</tt> cookie attribute handler for RFC 2965 cookie spec.
1002   */

1003  private class CookieCommentUrlAttributeHandler
1004          implements CookieAttributeHandler {
1005
1006      public void parse(final Cookie cookie, final String JavaDoc commenturl)
1007              throws MalformedCookieException {
1008          if (cookie instanceof Cookie2) {
1009              Cookie2 cookie2 = (Cookie2) cookie;
1010              cookie2.setCommentURL(commenturl);
1011          }
1012      }
1013
1014      public void validate(final Cookie cookie, final CookieOrigin origin)
1015              throws MalformedCookieException {
1016      }
1017
1018      public boolean match(final Cookie cookie, final CookieOrigin origin) {
1019          return true;
1020      }
1021      
1022  }
1023
1024  /**
1025   * <tt>"Discard"</tt> cookie attribute handler for RFC 2965 cookie spec.
1026   */

1027  private class CookieDiscardAttributeHandler
1028          implements CookieAttributeHandler {
1029
1030      public void parse(final Cookie cookie, final String JavaDoc commenturl)
1031              throws MalformedCookieException {
1032          if (cookie instanceof Cookie2) {
1033              Cookie2 cookie2 = (Cookie2) cookie;
1034              cookie2.setDiscard(true);
1035          }
1036      }
1037
1038      public void validate(final Cookie cookie, final CookieOrigin origin)
1039              throws MalformedCookieException {
1040      }
1041
1042      public boolean match(final Cookie cookie, final CookieOrigin origin) {
1043          return true;
1044      }
1045      
1046  }
1047
1048  /**
1049     * <tt>"Version"</tt> cookie attribute handler for RFC 2965 cookie spec.
1050     */

1051    private class Cookie2VersionAttributeHandler
1052            implements CookieAttributeHandler {
1053
1054        /**
1055         * Parse cookie version attribute.
1056         */

1057        public void parse(final Cookie cookie, final String JavaDoc value)
1058                throws MalformedCookieException {
1059            if (cookie == null) {
1060                throw new IllegalArgumentException JavaDoc("Cookie may not be null");
1061            }
1062            if (cookie instanceof Cookie2) {
1063                Cookie2 cookie2 = (Cookie2) cookie;
1064                if (value == null) {
1065                    throw new MalformedCookieException(
1066                            "Missing value for version attribute");
1067                }
1068                int version = -1;
1069                try {
1070                    version = Integer.parseInt(value);
1071                } catch (NumberFormatException JavaDoc e) {
1072                    version = -1;
1073                }
1074                if (version < 0) {
1075                    throw new MalformedCookieException("Invalid cookie version.");
1076                }
1077                cookie2.setVersion(version);
1078                cookie2.setVersionAttributeSpecified(true);
1079            }
1080        }
1081
1082        /**
1083         * validate cookie version attribute. Version attribute is REQUIRED.
1084         */

1085        public void validate(final Cookie cookie, final CookieOrigin origin)
1086                throws MalformedCookieException {
1087            if (cookie == null) {
1088                throw new IllegalArgumentException JavaDoc("Cookie may not be null");
1089            }
1090            if (cookie instanceof Cookie2) {
1091                Cookie2 cookie2 = (Cookie2) cookie;
1092                if (!cookie2.isVersionAttributeSpecified()) {
1093                    throw new MalformedCookieException(
1094                            "Violates RFC 2965. Version attribute is required.");
1095                }
1096            }
1097        }
1098
1099        public boolean match(final Cookie cookie, final CookieOrigin origin) {
1100            return true;
1101        }
1102
1103    }
1104
1105    public int getVersion() {
1106        return 1;
1107    }
1108
1109    public Header getVersionHeader() {
1110        ParameterFormatter formatter = new ParameterFormatter();
1111        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
1112        formatter.format(buffer, new NameValuePair("$Version",
1113                Integer.toString(getVersion())));
1114        return new Header("Cookie2", buffer.toString(), true);
1115    }
1116    
1117}
1118
1119
Popular Tags