001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2010–2011 ApexIdentity Inc.
015 * Portions Copyright 2011-2015 ForgeRock AS.
016 */
017
018package org.forgerock.http.protocol;
019
020import java.util.ArrayList;
021import java.util.Date;
022import java.util.List;
023
024/**
025 * An HTTP cookie. For more information, see the original <a href=
026 * "http://web.archive.org/web/20070805052634/http://wp.netscape.com/newsref/std/cookie_spec.html"
027 * > Netscape specification</a>, <a
028 * href="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</a> and <a
029 * href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a>.
030 */
031public class Cookie {
032    /** The name of the cookie. */
033    private String name;
034
035    /** The value of the cookie. */
036    private String value;
037
038    /** The intended use of a cookie. */
039    private String comment;
040
041    /** URL identifying the intended use of a cookie. */
042    private String commentURL;
043
044    /**
045     * Directs the user agent to discard the cookie unconditionally when it
046     * terminates.
047     */
048    private Boolean discard;
049
050    /** The domain for which the cookie is valid. */
051    private String domain;
052
053    /**
054     * The lifetime of the cookie, expressed as the date and time of expiration.
055     */
056    private Date expires;
057
058    /**
059     * Directs the user agent to make the cookie inaccessible to client side
060     * script.
061     */
062    private Boolean httpOnly;
063
064    /** The lifetime of the cookie, expressed in seconds. */
065    private Integer maxAge;
066
067    /** The subset of URLs on the origin server to which this cookie applies. */
068    private String path;
069
070    /** Restricts the port(s) to which a cookie may be returned. */
071    private final List<Integer> port = new ArrayList<>();
072
073    /**
074     * Directs the user agent to use only secure means to send back this cookie.
075     */
076    private Boolean secure;
077
078    /**
079     * The version of the state management mechanism to which this cookie
080     * conforms.
081     */
082    private Integer version;
083
084    /**
085     * Creates a new uninitialized cookie.
086     */
087    public Cookie() {
088        // Empty cookie.
089    }
090
091    @Override
092    public boolean equals(final Object obj) {
093        if (this == obj) {
094            return true;
095        }
096        if (!(obj instanceof Cookie)) {
097            return false;
098        }
099        final Cookie other = (Cookie) obj;
100        return objectsAreEqual(comment, other.comment)
101                && objectsAreEqual(commentURL, other.commentURL)
102                && objectsAreEqual(discard, other.discard)
103                && objectsAreEqual(domain, other.domain)
104                && objectsAreEqual(expires, other.expires)
105                && objectsAreEqual(httpOnly, other.httpOnly)
106                && objectsAreEqual(maxAge, other.maxAge)
107                && objectsAreEqual(name, other.name)
108                && objectsAreEqual(path, other.path)
109                && objectsAreEqual(port, other.port)
110                && objectsAreEqual(secure, other.secure)
111                && objectsAreEqual(value, other.value)
112                && objectsAreEqual(version, other.version);
113    }
114
115    /**
116     * Returns the intended use of a cookie.
117     *
118     * @return The intended use of a cookie.
119     */
120    public String getComment() {
121        return comment;
122    }
123
124    /**
125     * Returns the URL identifying the intended use of a cookie.
126     *
127     * @return The URL identifying the intended use of a cookie.
128     */
129    public String getCommentURL() {
130        return commentURL;
131    }
132
133    /**
134     * Returns {@code true} if the user agent should discard the cookie
135     * unconditionally when it terminates.
136     *
137     * @return {@code true} if the user agent should discard the cookie
138     *         unconditionally when it terminates.
139     */
140    public Boolean getDiscard() {
141        return discard;
142    }
143
144    /**
145     * Returns the domain for which the cookie is valid.
146     *
147     * @return The domain for which the cookie is valid.
148     */
149    public String getDomain() {
150        return domain;
151    }
152
153    /**
154     * Returns the lifetime of the cookie, expressed as the date and time of
155     * expiration.
156     *
157     * @return The lifetime of the cookie, expressed as the date and time of
158     *         expiration.
159     */
160    public Date getExpires() {
161        return expires;
162    }
163
164    /**
165     * Returns {@code true} if the user agent should make the cookie
166     * inaccessible to client side script.
167     *
168     * @return {@code true} if the user agent should make the cookie
169     *         inaccessible to client side script.
170     */
171    public Boolean isHttpOnly() {
172        return httpOnly == null ? false : httpOnly;
173    }
174
175    /**
176     * Returns the lifetime of the cookie, expressed in seconds.
177     *
178     * @return The lifetime of the cookie, expressed in seconds.
179     */
180    public Integer getMaxAge() {
181        return maxAge;
182    }
183
184    /**
185     * Returns name of the cookie.
186     *
187     * @return The name of the cookie.
188     */
189    public String getName() {
190        return name;
191    }
192
193    /**
194     * Returns the subset of URLs on the origin server to which this cookie
195     * applies.
196     *
197     * @return The subset of URLs on the origin server to which this cookie
198     *         applies.
199     */
200    public String getPath() {
201        return path;
202    }
203
204    /**
205     * Returns the restricted list of port(s) to which a cookie may be returned.
206     *
207     * @return The restricted list of port(s) to which a cookie may be returned.
208     */
209    public List<Integer> getPort() {
210        return port;
211    }
212
213    /**
214     * Returns {@code true} if the user agent should use only secure means to
215     * send back this cookie.
216     *
217     * @return {@code true} if the user agent should use only secure means to
218     *         send back this cookie.
219     */
220    public Boolean isSecure() {
221        return secure == null ? false : secure;
222    }
223
224    /**
225     * Returns the value of the cookie.
226     *
227     * @return The value of the cookie.
228     */
229    public String getValue() {
230        return value;
231    }
232
233    /**
234     * Returns the version of the state management mechanism to which this
235     * cookie conforms.
236     *
237     * @return The version of the state management mechanism to which this
238     *         cookie conforms.
239     */
240    public Integer getVersion() {
241        return version;
242    }
243
244    @Override
245    public int hashCode() {
246        final int prime = 31;
247        int result = 1;
248        result = prime * result + (comment == null ? 0 : comment.hashCode());
249        result = prime * result + (commentURL == null ? 0 : commentURL.hashCode());
250        result = prime * result + (discard == null ? 0 : discard.hashCode());
251        result = prime * result + (domain == null ? 0 : domain.hashCode());
252        result = prime * result + (expires == null ? 0 : expires.hashCode());
253        result = prime * result + (httpOnly == null ? 0 : httpOnly.hashCode());
254        result = prime * result + (maxAge == null ? 0 : maxAge.hashCode());
255        result = prime * result + (name == null ? 0 : name.hashCode());
256        result = prime * result + (path == null ? 0 : path.hashCode());
257        result = prime * result + (port == null ? 0 : port.hashCode());
258        result = prime * result + (secure == null ? 0 : secure.hashCode());
259        result = prime * result + (value == null ? 0 : value.hashCode());
260        result = prime * result + (version == null ? 0 : version.hashCode());
261        return result;
262    }
263
264    /**
265     * Sets the intended use of a cookie.
266     *
267     * @param comment
268     *            The intended use of a cookie.
269     * @return This cookie.
270     */
271    public Cookie setComment(final String comment) {
272        this.comment = comment;
273        return this;
274    }
275
276    /**
277     * Sets the URL identifying the intended use of a cookie.
278     *
279     * @param commentURL
280     *            The URL identifying the intended use of a cookie.
281     * @return This cookie.
282     */
283    public Cookie setCommentURL(final String commentURL) {
284        this.commentURL = commentURL;
285        return this;
286    }
287
288    /**
289     * Sets the value indicating whether the user agent should discard the
290     * cookie unconditionally when it terminates.
291     *
292     * @param discard
293     *            {@code true} if the user agent should discard the cookie
294     *            unconditionally when it terminates.
295     * @return This cookie.
296     */
297    public Cookie setDiscard(final Boolean discard) {
298        this.discard = discard;
299        return this;
300    }
301
302    /**
303     * Sets the domain for which the cookie is valid.
304     *
305     * @param domain
306     *            The domain for which the cookie is valid.
307     * @return This cookie.
308     */
309    public Cookie setDomain(final String domain) {
310        this.domain = domain;
311        return this;
312    }
313
314    /**
315     * Sets the lifetime of the cookie, expressed as the date and time of
316     * expiration.
317     *
318     * @param expires
319     *            The lifetime of the cookie, expressed as the date and time of
320     *            expiration.
321     * @return This cookie.
322     */
323    public Cookie setExpires(final Date expires) {
324        this.expires = expires;
325        return this;
326    }
327
328    /**
329     * Sets the value indicating whether the user agent should make the cookie
330     * inaccessible to client side script.
331     *
332     * @param httpOnly
333     *            {@code true} if the user agent should make the cookie
334     *            inaccessible to client side script.
335     * @return this;
336     */
337    public Cookie setHttpOnly(final Boolean httpOnly) {
338        this.httpOnly = httpOnly;
339        return this;
340    }
341
342    /**
343     * Sets the lifetime of the cookie, expressed in seconds.
344     *
345     * @param maxAge
346     *            The lifetime of the cookie, expressed in seconds.
347     * @return This cookie.
348     */
349    public Cookie setMaxAge(final Integer maxAge) {
350        this.maxAge = maxAge;
351        return this;
352    }
353
354    /**
355     * Sets the name of the cookie.
356     *
357     * @param name
358     *            The name of the cookie.
359     * @return This cookie.
360     */
361    public Cookie setName(final String name) {
362        this.name = name;
363        return this;
364    }
365
366    /**
367     * Sets the subset of URLs on the origin server to which this cookie
368     * applies.
369     *
370     * @param path
371     *            The subset of URLs on the origin server to which this cookie
372     *            applies.
373     * @return This cookie.
374     */
375    public Cookie setPath(final String path) {
376        this.path = path;
377        return this;
378    }
379
380    /**
381     * Sets the value indicating whether the user agent should use only secure
382     * means to send back this cookie.
383     *
384     * @param secure
385     *            {@code true} if the user agent should use only secure means to
386     *            send back this cookie.
387     * @return This cookie.
388     */
389    public Cookie setSecure(final Boolean secure) {
390        this.secure = secure;
391        return this;
392    }
393
394    /**
395     * Sets the value of the cookie.
396     *
397     * @param value
398     *            The value of the cookie.
399     * @return This cookie.
400     */
401    public Cookie setValue(final String value) {
402        this.value = value;
403        return this;
404    }
405
406    /**
407     * Sets the version of the state management mechanism to which this cookie
408     * conforms.
409     *
410     * @param version
411     *            The version of the state management mechanism to which this
412     *            cookie conforms.
413     * @return This cookie.
414     */
415    public Cookie setVersion(final Integer version) {
416        this.version = version;
417        return this;
418    }
419
420    @Override
421    public String toString() {
422        final StringBuilder builder = new StringBuilder();
423        builder.append("[");
424        if (name != null) {
425            builder.append("name=").append(name).append(" ");
426        }
427        if (value != null) {
428            builder.append("value=").append(value).append(" ");
429        }
430        if (comment != null) {
431            builder.append("comment=").append(comment).append(" ");
432        }
433        if (commentURL != null) {
434            builder.append("commentURL=").append(commentURL).append(" ");
435        }
436        if (discard != null) {
437            builder.append("discard=").append(discard).append(" ");
438        }
439        if (domain != null) {
440            builder.append("domain=").append(domain).append(" ");
441        }
442        if (expires != null) {
443            builder.append("expires=").append(expires).append(" ");
444        }
445        if (httpOnly != null) {
446            builder.append("httpOnly=").append(httpOnly).append(" ");
447        }
448        if (maxAge != null) {
449            builder.append("maxAge=").append(maxAge).append(" ");
450        }
451        if (path != null) {
452            builder.append("path=").append(path).append(" ");
453        }
454        if (port != null) {
455            builder.append("port=").append(port).append(" ");
456        }
457        if (secure != null) {
458            builder.append("secure=").append(secure).append(" ");
459        }
460        if (version != null) {
461            builder.append("version=").append(version);
462        }
463        builder.append("]");
464        return builder.toString();
465    }
466
467    private static boolean objectsAreEqual(final Object o1, final Object o2) {
468        if (o1 == null) {
469            return o2 == null;
470        } else {
471            return o1.equals(o2);
472        }
473    }
474
475}