Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/sonalb/net/http/cookie/Cookie.java


1   /*
2    * -*- mode: java; c-basic-indent: 4; indent-tabs-mode: nil -*-
3    * :indentSize=4:noTabs=true:tabSize=4:indentOnTab=true:indentOnEnter=true:mode=java:
4    * ex: set tabstop=4 expandtab:
5    *
6    * MrPostman - webmail <-> email gateway
7    * Copyright (C) 2002-2003 MrPostman Development Group
8    * Projectpage: http://mrbook.org/mrpostman/
9    *
10   *
11   * This program is free software; you can redistribute it and/or modify
12   * it under the terms of the GNU General Public License as published by
13   * the Free Software Foundation; either version 2 of the License, or
14   * (at your option) any later version.
15   *
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * In particular, this implies that users are responsible for
21   * using MrPostman after reading the terms and conditions given
22   * by their web-mail provider.
23   *
24   * You should have received a copy of the GNU General Public License
25   * Named LICENSE in the base directory of this distribution,
26   * if not, write to the Free Software
27   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28   */
29  
30  package com.sonalb.net.http.cookie;
31  
32  import com.sonalb.Utils;
33  
34  import java.net.URL;
35  
36  import java.util.Date;
37  
38  
39  /*
40  
41      /1.) Portlist determination from requestURL
42  
43      /2.) MaxAge setting requires Base time
44  
45      /3.) Expires is null only if maxage=0
46  
47      /4.) secure determined from Protocol (HTTPS eg.)
48  
49      /5.) maxage is -ve only if version=0
50  
51      /6.) Right now an IP address instead of Fully Qualified Domain Name would
52  
53          throw spanner in domain matching ... must discern between IP and name.
54  
55      7.) Path matching is case-sensitive. Follows from "Cookie Management" section of RFC2965.
56  
57      8.) If max-age is set to value other than 0 then discard should be false, unless explicitly set.
58  
59  
60  
61  NOTE:
62  
63  1.) A cookie with domain=.foo.com will be sent by client to host=y.x.foo.com . However,
64  
65  a cookie sent by y.x.foo.com with Domain=.foo.com will be rejected.
66  
67  2.) sendWith(URL) should not take into consideration whether cookie has expired. Document this.
68  
69  3.) Calling setExpires(Date) explicitly causes Cookie version to be reset to '0'
70  
71      3.1) Fixed ... USOE thrown if version=1 and setExpires called (To maintain consistency). getExpires is allowed for Version 1.
72  
73  4.) Right now an UnsupportedOpExc is thrown only when get/setMaxAge is called on a Version 0 cookie.
74  
75  V0 also does not support comment,commentURL,discard,port. Do we need to throw exc for these ?
76  
77      4.1) Made tough cookie ... throws USOE now. However, comment & commentURL field is allowed for version 0 cookie. Just in
78  
79          case some extra info is required to be stored.
80  
81  5.) According to RFC2965, if Set-Cookie and set-cookie2 both describe the same cookie, then sc2 should be used.
82  
83      This distinction has not been incorporated.
84  
85  
86  
87  */
88  
89  /**
90  
91   * The data structure representing a cookie. Supports both Netscape (Version 0) and RFC2965 (Version 1)
92  
93   * cookies. The fields common to both these versions are listed below:
94  
95   * <ul>
96  
97   * <li>NAME   - Must be set, no default value</li>
98  
99   * <li>VALUE  - Default value: Empty</li>
100 
101  * <li>Domain - Must be set, default: Local</li>
102 
103  * <li>Path   - Must be set, default: / (root)</li>
104 
105  * <li>Secure - Optional, default: false</li>
106 
107  * </ul>
108 
109  * @author    Sonal Bansal
110 
111  */
112 public class Cookie implements Cloneable, java.io.Serializable, Comparable {
113     public static final String CVSID = "$Id: Cookie.java,v 1.5 2003/02/09 23:38:11 lbruand Exp $";
114     private String name;
115     private String value;
116     private String comment;
117     private URL commentURL;
118     private boolean discard;
119     private String domain;
120     private int maxage;
121     private String path;
122     private String portList;
123     private boolean secure;
124     private String version;
125     private Date expires;
126     private Object lockSecure;
127     private Object lockDiscard;
128     private Object lockMaxage;
129     private boolean bExplicitDomain;
130     private boolean bExplicitExpires;
131     private boolean bExplicitMaxage;
132     private boolean bExplicitPath;
133     private boolean bExplicitPort;
134     private boolean bPortListSpecified;
135 
136     /**
137 
138      * Creates cookie instance.
139 
140      *
141 
142      * @param name the Cookie name
143 
144      * @param value the Cookie value
145 
146      * @param domain the domain in which this Cookie is valid
147 
148      * @param path the path for this Cookie
149 
150      */
151     public Cookie(String name, String value, String domain, String path) {
152         this(name, value, domain, path, null);
153     }
154 
155     /**
156 
157      * Creates cookie instance. The path and domain are picked up from the request URL.
158 
159       * @param name the Cookie name
160 
161      * @param value the Cookie value
162 
163      * @param requestURL the request URL which resulted in this cookie being received
164 
165      */
166     public Cookie(String name, String value, URL requestURL) {
167         this(name, value, null, null, requestURL);
168     }
169 
170     protected Cookie(String name, String value, String domain, String path, URL requestURL) {
171         initializeFields();
172 
173         setName(name);
174 
175         setValue(value);
176 
177         setDomain(domain, requestURL);
178 
179         setPath(path, requestURL);
180 
181         if (requestURL != null) {
182             setPort(requestURL.getPort());
183 
184             String protocol = requestURL.getProtocol();
185 
186             if (!Utils.isNullOrWhiteSpace(protocol)) {
187                 protocol = protocol.toLowerCase();
188 
189                 if ("https".equals(protocol) || "shttp".equals(protocol)) {
190                     secure = true;
191                 }
192             }
193         }
194     }
195 
196     Cookie() {
197         initializeFields();
198     }
199 
200     private void initializeFields() {
201         name = new String();
202 
203         value = new String();
204 
205         comment = new String();
206 
207         commentURL = null;
208 
209         discard = false;
210 
211         domain = new String();
212 
213         maxage = -1;
214 
215         path = new String();
216 
217         portList = new String();
218 
219         secure = false;
220 
221         version = "1";
222 
223         expires = new Date(0);
224 
225         lockSecure = new Object();
226 
227         lockDiscard = new Object();
228 
229         lockMaxage = new Object();
230 
231         bExplicitDomain = bExplicitExpires = bExplicitMaxage = bExplicitPath = bExplicitPort = bPortListSpecified = false;
232     }
233 
234     public Object clone() throws CloneNotSupportedException {
235         return (super.clone());
236     }
237 
238     /**
239 
240      * Compares one Cookie with another. The natural ordering is such that it follows the path
241 
242      * specificity rule laid down in RFC2695. Thus more specific path ("/acme/corp") comes
243 
244      * before ("is less than") less specific path ("/acme").
245 
246      */
247     public int compareTo(Object o) {
248         if ((o == null) || !(o instanceof Cookie)) {
249             throw new ClassCastException("Object is null or not a Cookie");
250         }
251 
252         if (equals(o)) {
253             return (0);
254         }
255 
256         Cookie c = (Cookie) o;
257 
258         if (!isValid() || !c.isValid()) {
259             throw new IllegalArgumentException("Either I am an invalid cookie or the argument is.");
260         }
261 
262         int mySlashes;
263         int hisSlashes;
264 
265         mySlashes = Utils.countInstances(getPath(), '/');
266 
267         hisSlashes = Utils.countInstances(c.getPath(), '/');
268 
269         if (mySlashes == hisSlashes) {
270             if (mySlashes == 1) {
271                 boolean bMine = "/".equals(getPath());
272 
273                 boolean bHis = "/".equals(c.getPath());
274 
275                 if (bMine || bHis) {
276                     if (!(bMine && bHis)) {
277                         if (bMine) {
278                             return (1);
279                         } else {
280                             return (-1);
281                         }
282                     }
283                 }
284             }
285 
286             return (0);
287         } else if (mySlashes > hisSlashes) {
288             return (-1);
289         }
290 
291         return (1);
292     }
293 
294     protected boolean isValid() {
295         synchronized (name) {
296             synchronized (domain) {
297                 synchronized (path) {
298                     if (Utils.isNullOrWhiteSpace(name) || Utils.isNullOrWhiteSpace(domain)
299                             || Utils.isNullOrWhiteSpace(path)) {
300                         return (false);
301                     }
302 
303                     return (true);
304                 }
305             }
306         }
307     }
308 
309     /**
310 
311      * Sets the Cookie name.
312 
313      * @param name the Cookie name
314 
315      */
316     public void setName(String name) {
317         if (Utils.isNullOrWhiteSpace(name)) {
318             throw new IllegalArgumentException("Cookie can't have empty name.");
319         }
320 
321         synchronized (this.name) {
322             this.name = name;
323         }
324     }
325 
326     /**
327 
328      * Sets the Cookie value.
329 
330      * @param value the Cookie value
331 
332      */
333     public void setValue(String value) {
334         synchronized (this.value) {
335             this.value = (value == null) ? "" : value;
336         }
337     }
338 
339     protected void setMaxAge(int maxage, Date base, boolean bInsider) {
340         if (!bInsider) {
341             if ("0".equals(version)) {
342                 throw new UnsupportedOperationException("Version 0 cookies do not support Max-Age");
343             }
344 
345             if ((maxage < 0) || (base == null)) {
346                 throw new IllegalArgumentException("Can't set max age");
347             }
348         }
349 
350         if (maxage == 0) {
351             synchronized (this.lockMaxage) {
352                 synchronized (this.expires) {
353                     synchronized (this.lockDiscard) {
354                         bExplicitMaxage = true;
355 
356                         this.maxage = 0;
357 
358                         bExplicitExpires = false;
359 
360                         expires = new Date(0);
361 
362                         discard = true;
363                     }
364                 }
365             }
366 
367             return;
368         }
369 
370         synchronized (this.lockMaxage) {
371             synchronized (this.expires) {
372                 bExplicitMaxage = true;
373 
374                 this.maxage = maxage;
375 
376                 long expiry = base.getTime() + (maxage * 1000);
377 
378                 expires = new Date(expiry);
379 
380                 bExplicitExpires = true;
381             }
382         }
383     }
384 
385     /**
386 
387      * Sets the lifetime of this Cookie. Applicable only to Version 1 cookies.
388 
389      * @param maxage the number of seconds from now that this Cookie is valid (delta-t)
390 
391      * @throws UnsupportedOperationException when called on a Version 0 cookie
392 
393      */
394     public void setMaxAge(int maxage) {
395         setMaxAge(maxage, new Date());
396     }
397 
398     /**
399 
400      * Sets the lifetime of this Cookie. Applicable only to Version 1 cookies.
401 
402      * @param maxage the number of seconds from base that this Cookie is valid (delta-t)
403 
404      * @param base the Date from which the delta-t should be counted
405 
406       * @throws UnsupportedOperationException when called on a Version 0 cookie
407 
408      */
409     public void setMaxAge(int maxage, Date base) {
410         setMaxAge(maxage, base, false);
411     }
412 
413     protected boolean explicitDomain() {
414         return (bExplicitDomain);
415     }
416 
417     protected boolean explicitPath() {
418         return (bExplicitPath);
419     }
420 
421     protected boolean explicitPort() {
422         return (bExplicitPort);
423     }
424 
425     protected boolean explicitExpires() {
426         return (bExplicitExpires);
427     }
428 
429     protected boolean explicitMaxage() {
430         return (bExplicitMaxage);
431     }
432 
433     protected boolean portListSpecified() {
434         return (bPortListSpecified);
435     }
436 
437     /**
438 
439      * Gets the amount of time this Cookie is valid, measured in seconds from the time the value was set.
440 
441      * Applicable only to Version 0 cookies.
442 
443      *
444 
445      * @return the delta-t that this Cookie holds valid
446 
447      * @throws UnsupportedOperationException if this method is called on a Version 0 cookie
448 
449      */
450     public int getMaxAge() {
451         if ("0".equals(version)) {
452             throw new UnsupportedOperationException("Version 0 cookies do not support Max-Age");
453         }
454 
455         if (!bExplicitMaxage) {
456             return (-1);
457         }
458 
459         synchronized (lockMaxage) {
460             return (maxage);
461         }
462     }
463 
464     /**
465 
466      * Sets the date-time when this cookie expires. Applicable only to Version 0 cookies.
467 
468      * @param expires the Date when this cookie expires
469 
470      * @throws UnsupportedOperationException when called on a Version 1 cookie
471 
472      */
473     public void setExpires(Date expires) {
474         if ("1".equals(version)) {
475             throw new UnsupportedOperationException("Version 1 cookies do not support Expires. Use Max-Age.");
476         }
477 
478         synchronized (this.expires) {
479             if (expires == null) {
480                 bExplicitExpires = false;
481 
482                 this.expires = new Date(0);
483             } else {
484                 bExplicitExpires = true;
485 
486                 this.expires = expires;
487             }
488         }
489     }
490 
491     /**
492 
493      * Sets the Cookie version. The version determines what fields and methods are valid for a
494 
495      * Cookie instance. It also determines the format in which the Cookie is sent with a request.
496 
497      * @param version the Cookie version. Either "0" or "1"
498 
499      */
500     public void setVersion(String version) {
501         if (!("0".equals(version) || "1".equals(version))) {
502             throw new IllegalArgumentException("Unsupported cookie version");
503         }
504 
505         synchronized (this.version) {
506             this.version = version;
507         }
508 
509         if ("0".equals(version)) {
510             bExplicitMaxage = false;
511         } else if (maxage == -1) {
512             bExplicitMaxage = false;
513         }
514     }
515 
516     /**
517 
518      * Gets the version of this Cookie.
519 
520      * @return the version; either "0" or "1"
521 
522      */
523     public String getVersion() {
524         synchronized (version) {
525             return (version);
526         }
527     }
528 
529     protected void setPath(String path, URL requestURL) {
530         if (!Utils.isNullOrWhiteSpace(path)) {
531             synchronized (this.path) {
532                 bExplicitPath = true;
533 
534                 path = path.trim();
535 
536                 if (path.charAt(0) != '/') {
537                     path = "/" + path;
538                 }
539 
540                 this.path = path;
541             }
542         } else {
543             synchronized (this.path) {
544                 bExplicitPath = false;
545 
546                 this.path = (requestURL == null) ? null : requestURL.getPath();
547 
548                 if (Utils.isNullOrWhiteSpace(this.path)) {
549                     this.path = "/";
550                 }
551 
552                 if (this.path.charAt(this.path.length() - 1) != '/') {
553                     this.path = this.path.substring(0, this.path.lastIndexOf('/') + 1);
554                 }
555             }
556         }
557     }
558 
559     /**
560 
561      * Sets the path for this Cookie.
562 
563      * @param path the Path for this Cookie
564 
565      */
566     public void setPath(String path) {
567         setPath(path, null);
568     }
569 
570     /**
571 
572      * Sets the path for this Cookie. Path is extracted from the URL.
573 
574      * @param requestURL the request URL which caused this Cookie to be sent.
575 
576      */
577     public void setPath(URL requestURL) {
578         setPath(null, requestURL);
579     }
580 
581     protected void setDomain(String domain, URL requestURL) {
582         synchronized (this.domain) {
583             if (!Utils.isNullOrWhiteSpace(domain)) {
584                 bExplicitDomain = true;
585 
586                 domain = domain.trim();
587 
588                 if (!Utils.isIPAddress(domain)) {
589                     if (domain.charAt(0) != '.') {
590                         domain = "." + domain;
591                     }
592                 }
593 
594                 this.domain = domain;
595             } else {
596                 bExplicitDomain = false;
597 
598                 this.domain = (requestURL == null) ? null : requestURL.getHost();
599 
600                 if (Utils.isNullOrWhiteSpace(this.domain)) {
601                     throw new IllegalArgumentException("Could not determine cookie domain.");
602                 }
603             }
604 
605             if (this.domain.indexOf('.') == -1) {
606                 this.domain += ".local";
607             }
608         }
609     }
610 
611     /**
612 
613      * Sets the domain for this Cookie. The domain determines which hosts can receive this Cookie.
614 
615      * @param domain the Cookie domain
616 
617      */
618     public void setDomain(String domain) {
619         setDomain(domain, null);
620     }
621 
622     /**
623 
624      * Sets the domain for this Cookie. The domain determines which hosts can receive this Cookie.
625 
626      * @param requestURL the request URL which caused this cookie to be sent
627 
628      */
629     public void setDomain(URL requestURL) {
630         setDomain(null, requestURL);
631     }
632 
633     /**
634 
635      * Sets the list of ports to which this Cookie can be sent. Applicable only to Version 1 cookies.
636 
637      * @param ports the valid ports as array of int; non-positive values ignored
638 
639      * @throws UnsupportedException when called on a Version 0 cookie
640 
641      */
642     public void setPortList(int[] ports) {
643         if ("0".equals(version)) {
644             throw new UnsupportedOperationException("Version 0 cookies do not support Port Matching");
645         }
646 
647         StringBuffer sb = new StringBuffer();
648 
649         for (int i = 0; i < ports.length; i++) {
650             if (ports[i] > 0) {
651                 sb.append(ports[i]);
652 
653                 sb.append(',');
654             }
655         }
656 
657         sb.deleteCharAt(sb.length() - 1);
658 
659         if (sb.length() <= 0) {
660             bExplicitPort = false;
661         }
662 
663         synchronized (portList) {
664             bExplicitPort = true;
665 
666             bPortListSpecified = true;
667 
668             portList = sb.toString();
669         }
670     }
671 
672     /**
673 
674      * Sets the port to which this cookie can be sent. Applicable only to Version 1 cookies.
675 
676      * @param p the Port
677 
678      * @throws UnsupportedOperationException when called on a Version 0 cookie
679 
680      */
681     public void setPort(int p) {
682         setPort(p, null);
683     }
684 
685     /**
686 
687      * Sets the port to which this cookie can be sent. Port is extracted from URL.
688 
689      * Applicable only to Version 1 cookies.
690 
691      * @param url the request URL
692 
693      * @throws UnsupportedOperationException when called on a Version 0 cookie
694 
695      */
696     public void setPort(URL url) {
697         setPort(-1, url);
698     }
699 
700     protected void setPort(int p, URL url) {
701         if ("0".equals(version)) {
702             throw new UnsupportedOperationException("Version 0 cookies do not support Port Matching.");
703         }
704 
705         if (p < 0) {
706             p = ((url == null) ? 80 : ((url.getPort() == -1) ? 80 : url.getPort()));
707 
708             bExplicitPort = false;
709         }
710 
711         synchronized (portList) {
712             bExplicitPort = true;
713 
714             portList = String.valueOf(p);
715         }
716     }
717 
718     /**
719 
720      * Gets the list of ports to which this cookie can be sent.
721 
722      * Applicable only to Version 1 cookies.
723 
724      * @return the comma-separated list of valid ports
725 
726      * @throws UnsupportedOperationException when called on a Version 0 cookie
727 
728      */
729     public String getPortList() {
730         if ("0".equals(version)) {
731             throw new UnsupportedOperationException("Version 0 cookies do not support Port Matching");
732         }
733 
734         synchronized (portList) {
735             return (portList);
736         }
737     }
738 
739     /**
740 
741      * Sets whether this cookie should be sent only over secure channels.
742 
743      * @param bSecure secure or not ?
744 
745      */
746     public void setSecure(boolean bSecure) {
747         synchronized (this.lockSecure) {
748             this.secure = bSecure;
749         }
750     }
751 
752     /**
753 
754      * Gets the name of this cookie.
755 
756      * @return the cookie name
757 
758      */
759     public String getName() {
760         synchronized (name) {
761             return (name);
762         }
763     }
764 
765     /**
766 
767      * Gets the value of this cookie.
768 
769      * @return the cookie value
770 
771      */
772     public String getValue() {
773         synchronized (value) {
774             return (value);
775         }
776     }
777 
778     /**
779 
780      * Gets the comment for this cookie.
781 
782      * @return the comment
783 
784      */
785     public String getComment() {
786         synchronized (comment) {
787             return (comment);
788         }
789     }
790 
791     /**
792 
793      * Sets the comment for this cookie. Comment has no functional value.
794 
795      * @param comment the comment
796 
797      */
798     public void setComment(String comment) {
799         synchronized (this.comment) {
800             this.comment = comment;
801         }
802     }
803 
804     /**
805 
806      * Sets the comment URL for this cookie. URL has no functional value.
807 
808      * @param url the URL
809 
810      */
811     public void setCommentURL(URL url) {
812         synchronized (this.commentURL) {
813             commentURL = url;
814         }
815     }
816 
817     /**
818 
819      * Gets the comment URL for this cookie.
820 
821      * @return the comment URL
822 
823      */
824     public URL getCommentURL() {
825         synchronized (commentURL) {
826             return (commentURL);
827         }
828     }
829 
830     /**
831 
832      * Gets the date-time when this cookie expires. Note that this can be called on both Version 0 AND
833 
834      * Version 1 cookies.
835 
836      * @return the date-time when this cookie expires
837 
838      */
839     public Date getExpires() {
840         if (!bExplicitExpires) {
841             return (null);
842         }
843 
844         synchronized (expires) {
845             return (expires);
846         }
847     }
848 
849     /**
850 
851      * Checks whether this cookie can be discarded once the session is over. This is different from the
852 
853      * lifetime of the cookie.
854 
855      * @return discardable or not ?
856 
857      */
858     public boolean isDiscardable() {
859         if ("0".equals(version)) {
860             throw new UnsupportedOperationException(
861                 "Version 0 cookies do not support Discard. Use hasExpired() instead");
862         }
863 
864         synchronized (this.lockDiscard) {
865             synchronized (lockMaxage) {
866                 return (discard || (maxage == 0));
867             }
868         }
869     }
870 
871     /**
872 
873      * Sets the discard status of this cookie. This determines whether the cookie is valid after the session
874 
875      * is over. It is different from lifetime. Applicable only to Version 1 cookies.
876 
877      * @param bDiscard discardable or not ?
878 
879      * @throws UnsupportedOperationException when called on a Version 0 cookie
880 
881      */
882     public void setDiscard(boolean bDiscard) {
883         if ("0".equals(version)) {
884             throw new UnsupportedOperationException("Version 0 cookies do not support Discard.");
885         }
886 
887         synchronized (this.lockDiscard) {
888             discard = bDiscard;
889         }
890     }
891 
892     /**
893 
894      * Gets the domain in which this cookie is valid.
895 
896      * @return the domain
897 
898      */
899     public String getDomain() {
900         synchronized (domain) {
901             return (domain);
902         }
903     }
904 
905     /**
906 
907      * Gets the path for this cookie.
908 
909      * @return the path
910 
911      */
912     public String getPath() {
913         synchronized (path) {
914             return (path);
915         }
916     }
917 
918     /**
919 
920      * Checks whether this cookie will be sent over secure channels only.
921 
922      * @return secure or not ?
923 
924      */
925     public boolean isSecure() {
926         synchronized (this.lockSecure) {
927             return (secure);
928         }
929     }
930 
931     /**
932 
933      * Checks whether this cookie's lifetime has expired or not. The lifetime has expired if:
934 
935      * <ul>
936 
937      * <li>The Max-Age (for Version 1 cookie) was explicitly set to 0
938 
939      * <li>The delta-t seconds set by Max-Age have passed
940 
941      * <li>The date-time now is greater than what was set to be the expiry date-time
942 
943      * </ul>
944 
945      * The current system time is used for lifetime calculation.<br>
946 
947      * If none of these conditions are satisfied, or if no explicit lifetime information was set
948 
949      * the cookie is deemed to not have expired.
950 
951      * @return expired or not ?
952 
953      */
954     public boolean hasExpired() {
955         return (hasExpired(new Date()));
956     }
957 
958     /**
959 
960      * Checks whether this cookie's lifetime has expired or not. The lifetime has expired if:
961 
962      * <ul>
963 
964      * <li>The Max-Age (for Version 1 cookie) was explicitly set to 0
965 
966      * <li>The delta-t seconds set by Max-Age have passed
967 
968      * <li>The date-time as specified by the input is greater than what was set to be the expiry date-time
969 
970      * </ul>
971 
972      * The input date-time is used for lifetime calculation.<br>
973 
974      * If none of these conditions are satisfied, or if no explicit lifetime information was set
975 
976      * the cookie is deemed to not have expired.
977 
978      * @return expired or not ?
979 
980      */
981     public boolean hasExpired(Date d) {
982         if (d == null) {
983             return (hasExpired());
984         }
985 
986         synchronized (expires) {
987             if (bExplicitExpires == false) {
988                 if (bExplicitMaxage && (maxage == 0)) {
989                     return (true);
990                 }
991 
992                 return (false);
993             }
994 
995             if ("0".equals(version)) {
996                 if (expires.getTime() < d.getTime()) {
997                     return (true);
998                 }
999 
1000                return (false);
1001            } else {
1002                if (expires.getTime() < d.getTime()) {
1003                    return (true);
1004                }
1005
1006                return (false);
1007            }
1008        }
1009    }
1010
1011    /**
1012
1013     * Checks whether two cookies are equal. Two cookies are deemed to be equal, when all of the
1014
1015     * following conditions are satisfied:
1016
1017     * <ul>
1018
1019     * <li>They have the same name</li>
1020
1021     * <li>They have the same domain</li>
1022
1023     * <li>They have the same path</li>
1024
1025     * </ul>
1026
1027     * Note that the cookie value is NOT used for equality determination.
1028
1029     */
1030    public boolean equals(Object obj) {
1031        if ((obj != null) && (obj instanceof Cookie)) {
1032            Cookie other = (Cookie) obj;
1033
1034            return (name.equalsIgnoreCase(other.getName()) && domain.equalsIgnoreCase(other.getDomain())
1035            && path.equals(other.getPath()));
1036        }
1037
1038        return (false);
1039    }
1040
1041    public String toString() {
1042        StringBuffer sb = new StringBuffer();
1043
1044        sb.append("[");
1045
1046        sb.append("VERSION=\"");
1047
1048        sb.append(version);
1049
1050        sb.append("\" ; NAME=\"");
1051
1052        sb.append(name);
1053
1054        sb.append("\" ; VALUE=\"");
1055
1056        sb.append(value);
1057
1058        sb.append("\" ; DOMAIN=\"");
1059
1060        sb.append(domain);
1061
1062        if ("1".equals(version)) {
1063            sb.append("\" ; PORTLIST=\"");
1064
1065            sb.append(portList);
1066        }
1067
1068        sb.append("\" ; PATH=\"");
1069
1070        sb.append(path);
1071
1072        if ("1".equals(version)) {
1073            sb.append("\" ; MAX-AGE=\"");
1074
1075            sb.append(maxage);
1076
1077            sb.append("\" ; DISCARD=\"");
1078
1079            sb.append(discard);
1080
1081            sb.append("\" ; COMMENT=\"");
1082
1083            sb.append(comment);
1084
1085            sb.append("\" ; COMMENTURL=\"");
1086
1087            sb.append((commentURL == null) ? "null" : commentURL.toString());
1088        }
1089
1090        sb.append("\" ; SECURE=\"");
1091
1092        sb.append(secure);
1093
1094        sb.append("\" ; EXPIRES=\"");
1095
1096        sb.append((bExplicitExpires ? expires.toString() : "null"));
1097
1098        sb.append("\"]");
1099
1100        return (sb.toString());
1101    }
1102}