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