001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package javax.servlet.http;
021    
022    import java.text.MessageFormat;
023    import java.util.ResourceBundle;
024    
025    /**
026     *
027     * Creates a cookie, a small amount of information sent by a servlet to 
028     * a Web browser, saved by the browser, and later sent back to the server.
029     * A cookie's value can uniquely 
030     * identify a client, so cookies are commonly used for session management.
031     * 
032     * <p>A cookie has a name, a single value, and optional attributes
033     * such as a comment, path and domain qualifiers, a maximum age, and a
034     * version number. Some Web browsers have bugs in how they handle the 
035     * optional attributes, so use them sparingly to improve the interoperability 
036     * of your servlets.
037     *
038     * <p>The servlet sends cookies to the browser by using the
039     * {@link HttpServletResponse#addCookie} method, which adds
040     * fields to HTTP response headers to send cookies to the 
041     * browser, one at a time. The browser is expected to 
042     * support 20 cookies for each Web server, 300 cookies total, and
043     * may limit cookie size to 4 KB each.
044     * 
045     * <p>The browser returns cookies to the servlet by adding 
046     * fields to HTTP request headers. Cookies can be retrieved
047     * from a request by using the {@link HttpServletRequest#getCookies} method.
048     * Several cookies might have the same name but different path attributes.
049     * 
050     * <p>Cookies affect the caching of the Web pages that use them. 
051     * HTTP 1.0 does not cache pages that use cookies created with
052     * this class. This class does not support the cache control
053     * defined with HTTP 1.1.
054     *
055     * <p>This class supports both the Version 0 (by Netscape) and Version 1 
056     * (by RFC 2109) cookie specifications. By default, cookies are
057     * created using Version 0 to ensure the best interoperability.
058     *
059     *
060     * @author      Various
061     * @version     $Version$
062     *
063     */
064    
065    // XXX would implement java.io.Serializable too, but can't do that
066    // so long as sun.servlet.* must run on older JDK 1.02 JVMs which
067    // don't include that support.
068    
069    public class Cookie implements Cloneable {
070    
071        private static final String LSTRING_FILE =
072            "javax.servlet.http.LocalStrings";
073        private static ResourceBundle lStrings =
074            ResourceBundle.getBundle(LSTRING_FILE);
075        
076        //
077        // The value of the cookie itself.
078        //
079        
080        private String name;        // NAME= ... "$Name" style is reserved
081        private String value;       // value of NAME
082    
083        //
084        // Attributes encoded in the header's cookie fields.
085        //
086        
087        private String comment;     // ;Comment=VALUE ... describes cookie's use
088                                    // ;Discard ... implied by maxAge < 0
089        private String domain;      // ;Domain=VALUE ... domain that sees cookie
090        private int maxAge = -1;    // ;Max-Age=VALUE ... cookies auto-expire
091        private String path;        // ;Path=VALUE ... URLs that see the cookie
092        private boolean secure;     // ;Secure ... e.g. use SSL
093        private int version = 0;    // ;Version=1 ... means RFC 2109++ style
094        
095        
096    
097        /**
098         * Constructs a cookie with a specified name and value.
099         *
100         * <p>The name must conform to RFC 2109. That means it can contain 
101         * only ASCII alphanumeric characters and cannot contain commas, 
102         * semicolons, or white space or begin with a $ character. The cookie's
103         * name cannot be changed after creation.
104         *
105         * <p>The value can be anything the server chooses to send. Its
106         * value is probably of interest only to the server. The cookie's
107         * value can be changed after creation with the
108         * <code>setValue</code> method.
109         *
110         * <p>By default, cookies are created according to the Netscape
111         * cookie specification. The version can be changed with the 
112         * <code>setVersion</code> method.
113         *
114         *
115         * @param name                      a <code>String</code> specifying the name of the cookie
116         *
117         * @param value                     a <code>String</code> specifying the value of the cookie
118         *
119         * @throws IllegalArgumentException if the cookie name contains illegal characters
120         *                                  (for example, a comma, space, or semicolon)
121         *                                  or it is one of the tokens reserved for use
122         *                                  by the cookie protocol
123         * @see #setValue
124         * @see #setVersion
125         *
126         */
127    
128        public Cookie(String name, String value) {
129            if (!isToken(name)
130                    || name.equalsIgnoreCase("Comment")     // rfc2019
131                    || name.equalsIgnoreCase("Discard")     // 2019++
132                    || name.equalsIgnoreCase("Domain")
133                    || name.equalsIgnoreCase("Expires")     // (old cookies)
134                    || name.equalsIgnoreCase("Max-Age")     // rfc2019
135                    || name.equalsIgnoreCase("Path")
136                    || name.equalsIgnoreCase("Secure")
137                    || name.equalsIgnoreCase("Version")
138                    || name.startsWith("$")
139                ) {
140                String errMsg = lStrings.getString("err.cookie_name_is_token");
141                Object[] errArgs = new Object[1];
142                errArgs[0] = name;
143                errMsg = MessageFormat.format(errMsg, errArgs);
144                throw new IllegalArgumentException(errMsg);
145            }
146    
147            this.name = name;
148            this.value = value;
149        }
150    
151    
152    
153    
154    
155        /**
156         *
157         * Specifies a comment that describes a cookie's purpose.
158         * The comment is useful if the browser presents the cookie 
159         * to the user. Comments
160         * are not supported by Netscape Version 0 cookies.
161         *
162         * @param purpose           a <code>String</code> specifying the comment 
163         *                          to display to the user
164         *
165         * @see #getComment
166         *
167         */
168    
169        public void setComment(String purpose) {
170            comment = purpose;
171        }
172        
173        
174        
175    
176        /**
177         * Returns the comment describing the purpose of this cookie, or
178         * <code>null</code> if the cookie has no comment.
179         *
180         * @return                  a <code>String</code> containing the comment,
181         *                          or <code>null</code> if none
182         *
183         * @see #setComment
184         *
185         */ 
186    
187        public String getComment() {
188            return comment;
189        }
190        
191        
192        
193    
194    
195        /**
196         *
197         * Specifies the domain within which this cookie should be presented.
198         *
199         * <p>The form of the domain name is specified by RFC 2109. A domain
200         * name begins with a dot (<code>.foo.com</code>) and means that
201         * the cookie is visible to servers in a specified Domain Name System
202         * (DNS) zone (for example, <code>www.foo.com</code>, but not 
203         * <code>a.b.foo.com</code>). By default, cookies are only returned
204         * to the server that sent them.
205         *
206         *
207         * @param pattern           a <code>String</code> containing the domain name
208         *                          within which this cookie is visible;
209         *                          form is according to RFC 2109
210         *
211         * @see #getDomain
212         *
213         */
214    
215        public void setDomain(String pattern) {
216            domain = pattern.toLowerCase(); // IE allegedly needs this
217        }
218        
219        
220        
221        
222    
223        /**
224         * Returns the domain name set for this cookie. The form of 
225         * the domain name is set by RFC 2109.
226         *
227         * @return                  a <code>String</code> containing the domain name
228         *
229         * @see #setDomain
230         *
231         */ 
232    
233        public String getDomain() {
234            return domain;
235        }
236    
237    
238    
239    
240        /**
241         * Sets the maximum age of the cookie in seconds.
242         *
243         * <p>A positive value indicates that the cookie will expire
244         * after that many seconds have passed. Note that the value is
245         * the <i>maximum</i> age when the cookie will expire, not the cookie's
246         * current age.
247         *
248         * <p>A negative value means
249         * that the cookie is not stored persistently and will be deleted
250         * when the Web browser exits. A zero value causes the cookie
251         * to be deleted.
252         *
253         * @param expiry            an integer specifying the maximum age of the
254         *                          cookie in seconds; if negative, means
255         *                          the cookie is not stored; if zero, deletes
256         *                          the cookie
257         *
258         *
259         * @see #getMaxAge
260         *
261         */
262    
263        public void setMaxAge(int expiry) {
264            maxAge = expiry;
265        }
266    
267    
268    
269    
270        /**
271         * Returns the maximum age of the cookie, specified in seconds,
272         * By default, <code>-1</code> indicating the cookie will persist
273         * until browser shutdown.
274         *
275         *
276         * @return                  an integer specifying the maximum age of the
277         *                          cookie in seconds; if negative, means
278         *                          the cookie persists until browser shutdown
279         *
280         *
281         * @see #setMaxAge
282         *
283         */
284    
285        public int getMaxAge() {
286            return maxAge;
287        }
288        
289        
290        
291    
292        /**
293         * Specifies a path for the cookie
294         * to which the client should return the cookie.
295         *
296         * <p>The cookie is visible to all the pages in the directory
297         * you specify, and all the pages in that directory's subdirectories. 
298         * A cookie's path must include the servlet that set the cookie,
299         * for example, <i>/catalog</i>, which makes the cookie
300         * visible to all directories on the server under <i>/catalog</i>.
301         *
302         * <p>Consult RFC 2109 (available on the Internet) for more
303         * information on setting path names for cookies.
304         *
305         *
306         * @param uri               a <code>String</code> specifying a path
307         *
308         *
309         * @see #getPath
310         *
311         */
312    
313        public void setPath(String uri) {
314            path = uri;
315        }
316    
317    
318    
319    
320        /**
321         * Returns the path on the server 
322         * to which the browser returns this cookie. The
323         * cookie is visible to all subpaths on the server.
324         *
325         *
326         * @return          a <code>String</code> specifying a path that contains
327         *                  a servlet name, for example, <i>/catalog</i>
328         *
329         * @see #setPath
330         *
331         */ 
332    
333        public String getPath() {
334            return path;
335        }
336    
337    
338    
339    
340    
341        /**
342         * Indicates to the browser whether the cookie should only be sent
343         * using a secure protocol, such as HTTPS or SSL.
344         *
345         * <p>The default value is <code>false</code>.
346         *
347         * @param flag      if <code>true</code>, sends the cookie from the browser
348         *                  to the server only when using a secure protocol;
349         *                  if <code>false</code>, sent on any protocol
350         *
351         * @see #getSecure
352         *
353         */
354     
355        public void setSecure(boolean flag) {
356            secure = flag;
357        }
358    
359    
360    
361    
362        /**
363         * Returns <code>true</code> if the browser is sending cookies
364         * only over a secure protocol, or <code>false</code> if the
365         * browser can send cookies using any protocol.
366         *
367         * @return          <code>true</code> if the browser uses a secure protocol;
368         *                   otherwise, <code>true</code>
369         *
370         * @see #setSecure
371         *
372         */
373    
374        public boolean getSecure() {
375            return secure;
376        }
377    
378    
379    
380    
381    
382        /**
383         * Returns the name of the cookie. The name cannot be changed after
384         * creation.
385         *
386         * @return          a <code>String</code> specifying the cookie's name
387         *
388         */
389    
390        public String getName() {
391            return name;
392        }
393    
394    
395    
396    
397    
398        /**
399         *
400         * Assigns a new value to a cookie after the cookie is created.
401         * If you use a binary value, you may want to use BASE64 encoding.
402         *
403         * <p>With Version 0 cookies, values should not contain white 
404         * space, brackets, parentheses, equals signs, commas,
405         * double quotes, slashes, question marks, at signs, colons,
406         * and semicolons. Empty values may not behave the same way
407         * on all browsers.
408         *
409         * @param newValue          a <code>String</code> specifying the new value 
410         *
411         *
412         * @see #getValue
413         * @see Cookie
414         *
415         */
416    
417        public void setValue(String newValue) {
418            value = newValue;
419        }
420    
421    
422    
423    
424        /**
425         * Returns the value of the cookie.
426         *
427         * @return                  a <code>String</code> containing the cookie's
428         *                          present value
429         *
430         * @see #setValue
431         * @see Cookie
432         *
433         */
434    
435        public String getValue() {
436            return value;
437        }
438    
439    
440    
441    
442        /**
443         * Returns the version of the protocol this cookie complies 
444         * with. Version 1 complies with RFC 2109, 
445         * and version 0 complies with the original
446         * cookie specification drafted by Netscape. Cookies provided
447         * by a browser use and identify the browser's cookie version.
448         * 
449         *
450         * @return                  0 if the cookie complies with the
451         *                          original Netscape specification; 1
452         *                          if the cookie complies with RFC 2109
453         *
454         * @see #setVersion
455         *
456         */
457    
458        public int getVersion() {
459            return version;
460        }
461    
462    
463    
464    
465        /**
466         * Sets the version of the cookie protocol this cookie complies
467         * with. Version 0 complies with the original Netscape cookie
468         * specification. Version 1 complies with RFC 2109.
469         *
470         * <p>Since RFC 2109 is still somewhat new, consider
471         * version 1 as experimental; do not use it yet on production sites.
472         *
473         *
474         * @param v                 0 if the cookie should comply with 
475         *                          the original Netscape specification;
476         *                          1 if the cookie should comply with RFC 2109
477         *
478         * @see #getVersion
479         *
480         */
481    
482        public void setVersion(int v) {
483            version = v;
484        }
485    
486        // Note -- disabled for now to allow full Netscape compatibility
487        // from RFC 2068, token special case characters
488        // 
489        // private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
490    
491        private static final String tspecials = ",; ";
492        
493        
494        
495    
496        /*
497         * Tests a string and returns true if the string counts as a 
498         * reserved token in the Java language.
499         * 
500         * @param value             the <code>String</code> to be tested
501         *
502         * @return                  <code>true</code> if the <code>String</code> is
503         *                          a reserved token; <code>false</code>
504         *                          if it is not                    
505         */
506    
507        private boolean isToken(String value) {
508            int len = value.length();
509    
510            for (int i = 0; i < len; i++) {
511                char c = value.charAt(i);
512    
513                if (c < 0x20 || c >= 0x7f || tspecials.indexOf(c) != -1)
514                    return false;
515            }
516            return true;
517        }
518    
519    
520    
521    
522    
523    
524        /**
525         *
526         * Overrides the standard <code>java.lang.Object.clone</code> 
527         * method to return a copy of this cookie.
528         *          
529         *
530         */
531    
532        public Object clone() {
533            try {
534                return super.clone();
535            } catch (CloneNotSupportedException e) {
536                throw new RuntimeException(e.getMessage());
537            }
538        }
539    }
540