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