View Javadoc

1   /*
2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/Cookie.java,v 1.44 2004/06/05 16:49:20 olegk Exp $
3    * $Revision: 157457 $
4    * $Date: 2005-03-14 15:23:16 -0500 (Mon, 14 Mar 2005) $
5    *
6    * ====================================================================
7    *
8    *  Copyright 1999-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;
31  
32  import java.io.Serializable;
33  import java.text.RuleBasedCollator;
34  import java.util.Comparator;
35  import java.util.Date;
36  import java.util.Locale;
37  
38  import org.apache.commons.httpclient.cookie.CookiePolicy;
39  import org.apache.commons.httpclient.cookie.CookieSpec;
40  import org.apache.commons.httpclient.util.LangUtils;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  
44  /***
45   * <p>
46   * HTTP "magic-cookie" represents a piece of state information
47   * that the HTTP agent and the target server can exchange to maintain 
48   * a session.
49   * </p>
50   * 
51   * @author B.C. Holmes
52   * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a>
53   * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
54   * @author Rod Waldhoff
55   * @author dIon Gillard
56   * @author Sean C. Sullivan
57   * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a>
58   * @author Marc A. Saegesser
59   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
60   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
61   * 
62   * @version $Revision: 157457 $ $Date: 2005-03-14 15:23:16 -0500 (Mon, 14 Mar 2005) $
63   */
64  public class Cookie extends NameValuePair implements Serializable, Comparator {
65  
66      // ----------------------------------------------------------- Constructors
67  
68      /***
69       * Default constructor. Creates a blank cookie 
70       */
71  
72      public Cookie() {
73          this(null, "noname", null, null, null, false);
74      }
75  
76      /***
77       * Creates a cookie with the given name, value and domain attribute.
78       *
79       * @param name    the cookie name
80       * @param value   the cookie value
81       * @param domain  the domain this cookie can be sent to
82       */
83      public Cookie(String domain, String name, String value) {
84          this(domain, name, value, null, null, false);
85      }
86  
87      /***
88       * Creates a cookie with the given name, value, domain attribute,
89       * path attribute, expiration attribute, and secure attribute 
90       *
91       * @param name    the cookie name
92       * @param value   the cookie value
93       * @param domain  the domain this cookie can be sent to
94       * @param path    the path prefix for which this cookie can be sent
95       * @param expires the {@link Date} at which this cookie expires,
96       *                or <tt>null</tt> if the cookie expires at the end
97       *                of the session
98       * @param secure if true this cookie can only be sent over secure
99       * connections
100      * @throws IllegalArgumentException If cookie name is null or blank,
101      *   cookie name contains a blank, or cookie name starts with character $
102      *   
103      */
104     public Cookie(String domain, String name, String value, 
105         String path, Date expires, boolean secure) {
106             
107         super(name, value);
108         LOG.trace("enter Cookie(String, String, String, String, Date, boolean)");
109         if (name == null) {
110             throw new IllegalArgumentException("Cookie name may not be null");
111         }
112         if (name.trim().equals("")) {
113             throw new IllegalArgumentException("Cookie name may not be blank");
114         }
115         this.setPath(path);
116         this.setDomain(domain);
117         this.setExpiryDate(expires);
118         this.setSecure(secure);
119     }
120 
121     /***
122      * Creates a cookie with the given name, value, domain attribute,
123      * path attribute, maximum age attribute, and secure attribute 
124      *
125      * @param name   the cookie name
126      * @param value  the cookie value
127      * @param domain the domain this cookie can be sent to
128      * @param path   the path prefix for which this cookie can be sent
129      * @param maxAge the number of seconds for which this cookie is valid.
130      *               maxAge is expected to be a non-negative number. 
131      *               <tt>-1</tt> signifies that the cookie should never expire.
132      * @param secure if <tt>true</tt> this cookie can only be sent over secure
133      * connections
134      */
135     public Cookie(String domain, String name, String value, String path, 
136         int maxAge, boolean secure) {
137             
138         this(domain, name, value, path, null, secure);
139         if (maxAge < -1) {
140             throw new IllegalArgumentException("Invalid max age:  " + Integer.toString(maxAge));
141         }            
142         if (maxAge >= 0) {
143             setExpiryDate(new Date(System.currentTimeMillis() + maxAge * 1000L));
144         }
145     }
146 
147     /***
148      * Returns the comment describing the purpose of this cookie, or
149      * <tt>null</tt> if no such comment has been defined.
150      * 
151      * @return comment 
152      *
153      * @see #setComment(String)
154      */
155     public String getComment() {
156         return cookieComment;
157     }
158 
159     /***
160      * If a user agent (web browser) presents this cookie to a user, the
161      * cookie's purpose will be described using this comment.
162      * 
163      * @param comment
164      *  
165      * @see #getComment()
166      */
167     public void setComment(String comment) {
168         cookieComment = comment;
169     }
170 
171     /***
172      * Returns the expiration {@link Date} of the cookie, or <tt>null</tt>
173      * if none exists.
174      * <p><strong>Note:</strong> the object returned by this method is 
175      * considered immutable. Changing it (e.g. using setTime()) could result
176      * in undefined behaviour. Do so at your peril. </p>
177      * @return Expiration {@link Date}, or <tt>null</tt>.
178      *
179      * @see #setExpiryDate(java.util.Date)
180      *
181      */
182     public Date getExpiryDate() {
183         return cookieExpiryDate;
184     }
185 
186     /***
187      * Sets expiration date.
188      * <p><strong>Note:</strong> the object returned by this method is considered
189      * immutable. Changing it (e.g. using setTime()) could result in undefined 
190      * behaviour. Do so at your peril.</p>
191      *
192      * @param expiryDate the {@link Date} after which this cookie is no longer valid.
193      *
194      * @see #getExpiryDate
195      *
196      */
197     public void setExpiryDate (Date expiryDate) {
198         cookieExpiryDate = expiryDate;
199     }
200 
201 
202     /***
203      * Returns <tt>false</tt> if the cookie should be discarded at the end
204      * of the "session"; <tt>true</tt> otherwise.
205      *
206      * @return <tt>false</tt> if the cookie should be discarded at the end
207      *         of the "session"; <tt>true</tt> otherwise
208      */
209     public boolean isPersistent() {
210         return (null != cookieExpiryDate);
211     }
212 
213 
214     /***
215      * Returns domain attribute of the cookie.
216      * 
217      * @return the value of the domain attribute
218      *
219      * @see #setDomain(java.lang.String)
220      */
221     public String getDomain() {
222         return cookieDomain;
223     }
224 
225     /***
226      * Sets the domain attribute.
227      * 
228      * @param domain The value of the domain attribute
229      *
230      * @see #getDomain
231      */
232     public void setDomain(String domain) {
233         if (domain != null) {
234             int ndx = domain.indexOf(":");
235             if (ndx != -1) {
236               domain = domain.substring(0, ndx);
237             }
238             cookieDomain = domain.toLowerCase();
239         }
240     }
241 
242 
243     /***
244      * Returns the path attribute of the cookie
245      * 
246      * @return The value of the path attribute.
247      * 
248      * @see #setPath(java.lang.String)
249      */
250     public String getPath() {
251         return cookiePath;
252     }
253 
254     /***
255      * Sets the path attribute.
256      *
257      * @param path The value of the path attribute
258      *
259      * @see #getPath
260      *
261      */
262     public void setPath(String path) {
263         cookiePath = path;
264     }
265 
266     /***
267      * @return <code>true</code> if this cookie should only be sent over secure connections.
268      * @see #setSecure(boolean)
269      */
270     public boolean getSecure() {
271         return isSecure;
272     }
273 
274     /***
275      * Sets the secure attribute of the cookie.
276      * <p>
277      * When <tt>true</tt> the cookie should only be sent
278      * using a secure protocol (https).  This should only be set when
279      * the cookie's originating server used a secure protocol to set the
280      * cookie's value.
281      *
282      * @param secure The value of the secure attribute
283      * 
284      * @see #getSecure()
285      */
286     public void setSecure (boolean secure) {
287         isSecure = secure;
288     }
289 
290     /***
291      * Returns the version of the cookie specification to which this
292      * cookie conforms.
293      *
294      * @return the version of the cookie.
295      * 
296      * @see #setVersion(int)
297      *
298      */
299     public int getVersion() {
300         return cookieVersion;
301     }
302 
303     /***
304      * Sets the version of the cookie specification to which this
305      * cookie conforms. 
306      *
307      * @param version the version of the cookie.
308      * 
309      * @see #getVersion
310      */
311     public void setVersion(int version) {
312         cookieVersion = version;
313     }
314 
315     /***
316      * Returns true if this cookie has expired.
317      * 
318      * @return <tt>true</tt> if the cookie has expired.
319      */
320     public boolean isExpired() {
321         return (cookieExpiryDate != null  
322             && cookieExpiryDate.getTime() <= System.currentTimeMillis());
323     }
324 
325     /***
326      * Returns true if this cookie has expired according to the time passed in.
327      * 
328      * @param now The current time.
329      * 
330      * @return <tt>true</tt> if the cookie expired.
331      */
332     public boolean isExpired(Date now) {
333         return (cookieExpiryDate != null  
334             && cookieExpiryDate.getTime() <= now.getTime());
335     }
336 
337 
338     /***
339      * Indicates whether the cookie had a path specified in a 
340      * path attribute of the <tt>Set-Cookie</tt> header. This value
341      * is important for generating the <tt>Cookie</tt> header because 
342      * some cookie specifications require that the <tt>Cookie</tt> header 
343      * should only include a path attribute if the cookie's path 
344      * was specified in the <tt>Set-Cookie</tt> header.
345      *
346      * @param value <tt>true</tt> if the cookie's path was explicitly 
347      * set, <tt>false</tt> otherwise.
348      * 
349      * @see #isPathAttributeSpecified
350      */
351     public void setPathAttributeSpecified(boolean value) {
352         hasPathAttribute = value;
353     }
354 
355     /***
356      * Returns <tt>true</tt> if cookie's path was set via a path attribute
357      * in the <tt>Set-Cookie</tt> header.
358      *
359      * @return value <tt>true</tt> if the cookie's path was explicitly 
360      * set, <tt>false</tt> otherwise.
361      * 
362      * @see #setPathAttributeSpecified
363      */
364     public boolean isPathAttributeSpecified() {
365         return hasPathAttribute;
366     }
367 
368     /***
369      * Indicates whether the cookie had a domain specified in a 
370      * domain attribute of the <tt>Set-Cookie</tt> header. This value
371      * is important for generating the <tt>Cookie</tt> header because 
372      * some cookie specifications require that the <tt>Cookie</tt> header 
373      * should only include a domain attribute if the cookie's domain 
374      * was specified in the <tt>Set-Cookie</tt> header.
375      *
376      * @param value <tt>true</tt> if the cookie's domain was explicitly 
377      * set, <tt>false</tt> otherwise.
378      *
379      * @see #isDomainAttributeSpecified
380      */
381     public void setDomainAttributeSpecified(boolean value) {
382         hasDomainAttribute = value;
383     }
384 
385     /***
386      * Returns <tt>true</tt> if cookie's domain was set via a domain 
387      * attribute in the <tt>Set-Cookie</tt> header.
388      *
389      * @return value <tt>true</tt> if the cookie's domain was explicitly 
390      * set, <tt>false</tt> otherwise.
391      *
392      * @see #setDomainAttributeSpecified
393      */
394     public boolean isDomainAttributeSpecified() {
395         return hasDomainAttribute;
396     }
397 
398     /***
399      * Returns a hash code in keeping with the
400      * {@link Object#hashCode} general hashCode contract.
401      * @return A hash code
402      */
403     public int hashCode() {
404         int hash = LangUtils.HASH_SEED;
405         hash = LangUtils.hashCode(hash, this.getName());
406         hash = LangUtils.hashCode(hash, this.cookieDomain);
407         hash = LangUtils.hashCode(hash, this.cookiePath);
408         return hash;
409     }
410 
411 
412     /***
413      * Two cookies are equal if the name, path and domain match.
414      * @param obj The object to compare against.
415      * @return true if the two objects are equal.
416      */
417     public boolean equals(Object obj) {
418         if (obj == null) return false;
419         if (this == obj) return true;
420         if (obj instanceof Cookie) {
421             Cookie that = (Cookie) obj;
422             return LangUtils.equals(this.getName(), that.getName())
423                   && LangUtils.equals(this.cookieDomain, that.cookieDomain)
424                   && LangUtils.equals(this.cookiePath, that.cookiePath);
425         } else {
426             return false;
427         }
428     }
429 
430 
431     /***
432      * Return a textual representation of the cookie.
433      * 
434      * @return string.
435      */
436     public String toExternalForm() {
437         CookieSpec spec = null;
438         if (getVersion() > 0) {
439             spec = CookiePolicy.getDefaultSpec(); 
440         } else {
441             spec = CookiePolicy.getCookieSpec(CookiePolicy.NETSCAPE); 
442         }
443         return spec.formatCookie(this); 
444     }
445 
446     /***
447      * <p>Compares two cookies to determine order for cookie header.</p>
448      * <p>Most specific should be first. </p>
449      * <p>This method is implemented so a cookie can be used as a comparator for
450      * a SortedSet of cookies. Specifically it's used above in the 
451      * createCookieHeader method.</p>
452      * @param o1 The first object to be compared
453      * @param o2 The second object to be compared
454      * @return See {@link java.util.Comparator#compare(Object,Object)}
455      */
456     public int compare(Object o1, Object o2) {
457         LOG.trace("enter Cookie.compare(Object, Object)");
458 
459         if (!(o1 instanceof Cookie)) {
460             throw new ClassCastException(o1.getClass().getName());
461         }
462         if (!(o2 instanceof Cookie)) {
463             throw new ClassCastException(o2.getClass().getName());
464         }
465         Cookie c1 = (Cookie) o1;
466         Cookie c2 = (Cookie) o2;
467         if (c1.getPath() == null && c2.getPath() == null) {
468             return 0;
469         } else if (c1.getPath() == null) {
470             // null is assumed to be "/"
471             if (c2.getPath().equals(CookieSpec.PATH_DELIM)) {
472                 return 0;
473             } else {
474                 return -1;
475             }
476         } else if (c2.getPath() == null) {
477             // null is assumed to be "/"
478             if (c1.getPath().equals(CookieSpec.PATH_DELIM)) {
479                 return 0;
480             } else {
481                 return 1;
482             }
483         } else {
484             return STRING_COLLATOR.compare(c1.getPath(), c2.getPath());
485         }
486     }
487 
488     /***
489      * Return a textual representation of the cookie.
490      * 
491      * @return string.
492      * 
493      * @see #toExternalForm
494      */
495     public String toString() {
496         return toExternalForm();
497     }
498 
499    // ----------------------------------------------------- Instance Variables
500 
501    /*** Comment attribute. */
502    private String  cookieComment;
503 
504    /*** Domain attribute. */
505    private String  cookieDomain;
506 
507    /*** Expiration {@link Date}. */
508    private Date    cookieExpiryDate;
509 
510    /*** Path attribute. */
511    private String  cookiePath;
512 
513    /*** My secure flag. */
514    private boolean isSecure;
515 
516    /***
517     * Specifies if the set-cookie header included a Path attribute for this
518     * cookie
519     */
520    private boolean hasPathAttribute = false;
521 
522    /***
523     * Specifies if the set-cookie header included a Domain attribute for this
524     * cookie
525     */
526    private boolean hasDomainAttribute = false;
527 
528    /*** The version of the cookie specification I was created from. */
529    private int     cookieVersion = 0;
530 
531    // -------------------------------------------------------------- Constants
532 
533    /*** 
534     * Collator for Cookie comparisons.  Could be replaced with references to
535     * specific Locales.
536     */
537    private static final RuleBasedCollator STRING_COLLATOR =
538         (RuleBasedCollator) RuleBasedCollator.getInstance(
539                                                 new Locale("en", "US", ""));
540 
541    /*** Log object for this class */
542    private static final Log LOG = LogFactory.getLog(Cookie.class);
543 
544 }
545