KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > HTTPClient > Cookie


1 /*
2  * @(#)Cookie.java 0.3-2 18/06/1999
3  *
4  * This file is part of the HTTPClient package
5  * Copyright (C) 1996-1999 Ronald Tschalär
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free
19  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307, USA
21  *
22  * For questions, suggestions, bug-reports, enhancement-requests etc.
23  * I may be contacted at:
24  *
25  * ronald@innovation.ch
26  *
27  */

28
29 package HTTPClient;
30
31 import java.io.File JavaDoc;
32 import java.net.ProtocolException JavaDoc;
33 import java.util.Date JavaDoc;
34 import java.util.Hashtable JavaDoc;
35
36
37 /**
38  * This class represents an http cookie as specified in
39  * <a HREF="http://home.netscape.com/newsref/std/cookie_spec.html">Netscape's
40  * cookie spec</a>
41  *
42  * @version 0.3-2 18/06/1999
43  * @author Ronald Tschalär
44  * @since V0.3
45  */

46
47 public class Cookie implements java.io.Serializable JavaDoc
48 {
49     protected String JavaDoc name;
50     protected String JavaDoc value;
51     protected Date JavaDoc expires;
52     protected String JavaDoc domain;
53     protected String JavaDoc path;
54     protected boolean secure;
55
56
57     /**
58      * Create a cookie.
59      *
60      * @param name the cookie name
61      * @param value the cookie value
62      * @param domain the host this cookie will be sent to
63      * @param path the path prefix for which this cookie will be sent
64      * @param epxires the Date this cookie expires, null if at end of
65      * session
66      * @param secure if true this cookie will only be over secure connections
67      * @exception NullPointerException if <var>name</var>, <var>value</var>,
68      * <var>domain</var>, or <var>path</var>
69      * is null
70      * @since V0.3-1
71      */

72     public Cookie(String JavaDoc name, String JavaDoc value, String JavaDoc domain, String JavaDoc path,
73           Date JavaDoc expires, boolean secure)
74     {
75     if (name == null) throw new NullPointerException JavaDoc("missing name");
76     if (value == null) throw new NullPointerException JavaDoc("missing value");
77     if (domain == null) throw new NullPointerException JavaDoc("missing domain");
78     if (path == null) throw new NullPointerException JavaDoc("missing path");
79
80     this.name = name;
81     this.value = value;
82     this.domain = domain.toLowerCase();
83     this.path = path;
84     this.expires = expires;
85     this.secure = secure;
86
87     if (this.domain.indexOf('.') == -1) this.domain += ".local";
88     }
89
90
91     /**
92      * Use <code>parse()</code> to create cookies.
93      *
94      * @see #parse(java.lang.String, HTTPClient.RoRequest)
95      */

96     protected Cookie(RoRequest req)
97     {
98     name = null;
99     value = null;
100     expires = null;
101     domain = req.getConnection().getHost();
102     if (domain.indexOf('.') == -1) domain += ".local";
103     path = Util.getPath(req.getRequestURI());
104
105     String JavaDoc prot = req.getConnection().getProtocol();
106     if (prot.equals("https") || prot.equals("shttp"))
107         secure = true;
108     else
109         secure = false;
110     }
111
112
113     /**
114      * Parses the Set-Cookie header into an array of Cookies.
115      *
116      * @param set_cookie the Set-Cookie header received from the server
117      * @param req the request used
118      * @return an array of Cookies as parsed from the Set-Cookie header
119      * @exception ProtocolException if an error occurs during parsing
120      */

121     protected static Cookie[] parse(String JavaDoc set_cookie, RoRequest req)
122         throws ProtocolException JavaDoc
123     {
124         int beg = 0,
125                end = 0,
126            start = 0;
127         char[] buf = set_cookie.toCharArray();
128         int len = buf.length;
129
130         Cookie cookie_arr[] = new Cookie[0], curr;
131
132
133         cookies: while (true) // get all cookies
134
{
135             beg = Util.skipSpace(buf, beg);
136             if (beg >= len) break; // no more left
137
if (buf[beg] == ',') // empty header
138
{
139         beg++;
140         continue;
141         }
142
143         curr = new Cookie(req);
144         start = beg;
145
146         boolean legal = true;
147
148         parts: while (true) // parse all parts
149
{
150         if (beg >= len || buf[beg] == ',') break;
151
152         // skip empty fields
153
if (buf[beg] == ';')
154         {
155             beg = Util.skipSpace(buf, beg+1);
156             continue;
157         }
158
159         // first check for secure, as this is the only one w/o a '='
160
if ((beg+6 <= len) &&
161             set_cookie.regionMatches(true, beg, "secure", 0, 6))
162         {
163             curr.secure = true;
164             beg += 6;
165
166             beg = Util.skipSpace(buf, beg);
167             if (beg < len && buf[beg] == ';') // consume ";"
168
beg = Util.skipSpace(buf, beg+1);
169             else if (beg < len && buf[beg] != ',')
170             throw new ProtocolException JavaDoc("Bad Set-Cookie header: " +
171                             set_cookie + "\nExpected " +
172                             "';' or ',' at position " +
173                             beg);
174
175             continue;
176         }
177
178         // alright, must now be of the form x=y
179
end = set_cookie.indexOf('=', beg);
180         if (end == -1)
181             throw new ProtocolException JavaDoc("Bad Set-Cookie header: " +
182                         set_cookie + "\nNo '=' found " +
183                         "for token starting at " +
184                         "position " + beg);
185
186         String JavaDoc name = set_cookie.substring(beg, end).trim();
187         beg = Util.skipSpace(buf, end+1);
188
189         if (name.equalsIgnoreCase("expires"))
190         {
191             /* cut off the weekday if it is there. This is a little
192              * tricky because the comma is also used between cookies
193              * themselves. To make sure we don't inadvertantly
194              * mistake a date for a weekday we only skip letters.
195              */

196             int pos = beg;
197             while (buf[pos] >= 'a' && buf[pos] <= 'z' ||
198                buf[pos] >= 'A' && buf[pos] <= 'Z')
199             pos++;
200             pos = Util.skipSpace(buf, pos);
201             if (buf[pos] == ',')
202             beg = pos+1;
203         }
204
205         int comma = set_cookie.indexOf(',', beg);
206         int semic = set_cookie.indexOf(';', beg);
207         if (comma == -1 && semic == -1) end = len;
208         else if (comma == -1) end = semic;
209         else if (semic == -1) end = comma;
210         else end = Math.min(comma, semic);
211
212         String JavaDoc value = set_cookie.substring(beg, end).trim();
213
214         if (name.equalsIgnoreCase("expires"))
215         {
216             try
217             { curr.expires = new Date JavaDoc(value); }
218             catch (IllegalArgumentException JavaDoc iae)
219             {
220             /* More broken servers to deal with... Ignore expires
221              * if it's invalid
222             throw new ProtocolException("Bad Set-Cookie header: "+
223                     set_cookie + "\nInvalid date found at "+
224                     "position " + beg);
225             */

226             }
227         }
228         else if (name.equalsIgnoreCase("domain"))
229         {
230             // domains are case insensitive.
231
value = value.toLowerCase();
232
233             // add leading dot, if missing
234
if (value.charAt(0) != '.' && !value.equals(curr.domain))
235             value = '.' + value;
236
237             // must be the same domain as in the url
238
if (!curr.domain.endsWith(value))
239             legal = false;
240
241
242             /* Netscape's original 2-/3-dot rule really doesn't work
243              * because many countries use a shallow hierarchy (similar
244              * to the special TLDs defined in the spec). While the
245              * rules in draft-ietf-http-state-man-mec-08 aren't
246              * perfect either, they are better. OTOH, some sites
247              * use a domain so that the host name minus the domain
248              * name contains a dot (e.g. host x.x.yahoo.com and
249              * domain .yahoo.com). So, for the seven special TLDs we
250              * use the 2-dot rule, and for all others we use the
251              * rules in the state-man draft instead.
252              */

253
254             // domain must be either .local or must contain at least
255
// two dots
256
if (!value.equals(".local") && value.indexOf('.', 1) == -1)
257             legal = false;
258
259             // If TLD not special then host minus domain may not
260
// contain any dots
261
String JavaDoc top = null;
262             if (value.length() > 3 )
263             top = value.substring(value.length()-4);
264             if (top == null || !(
265             top.equalsIgnoreCase(".com") ||
266             top.equalsIgnoreCase(".edu") ||
267             top.equalsIgnoreCase(".net") ||
268             top.equalsIgnoreCase(".org") ||
269             top.equalsIgnoreCase(".gov") ||
270             top.equalsIgnoreCase(".mil") ||
271             top.equalsIgnoreCase(".int")))
272             {
273             int dl = curr.domain.length(), vl = value.length();
274             if (dl > vl &&
275                 curr.domain.substring(0, dl-vl).indexOf('.') != -1)
276                 legal = false;
277             }
278
279             curr.domain = value;
280         }
281         else if (name.equalsIgnoreCase("path"))
282             curr.path = value;
283         else
284         {
285             curr.name = name;
286             curr.value = value;
287         }
288
289         beg = end;
290         if (beg < len && buf[beg] == ';') // consume ";"
291
beg = Util.skipSpace(buf, beg+1);
292         }
293
294         if (curr.name == null || curr.value == null)
295                 throw new ProtocolException JavaDoc("Bad Set-Cookie header: " +
296                         set_cookie + "\nNo Name=Value found"
297                         + " for cookie starting at " +
298                         "posibition " + start);
299
300         if (legal)
301         {
302         cookie_arr = Util.resizeArray(cookie_arr, cookie_arr.length+1);
303         cookie_arr[cookie_arr.length-1] = curr;
304         }
305     }
306
307     return cookie_arr;
308     }
309
310
311     /**
312      * Return the name of this cookie.
313      */

314     public String JavaDoc getName()
315     {
316     return name;
317     }
318
319
320     /**
321      * Return the value of this cookie.
322      */

323     public String JavaDoc getValue()
324     {
325     return value;
326     }
327
328
329     /**
330      * @return the expiry date of this cookie, or null if none set.
331      */

332     public Date JavaDoc expires()
333     {
334     return expires;
335     }
336
337
338     /**
339      * @return true if the cookie should be discarded at the end of the
340      * session; false otherwise
341      */

342     public boolean discard()
343     {
344     return (expires == null);
345     }
346
347
348     /**
349      * Return the domain this cookie is valid in.
350      */

351     public String JavaDoc getDomain()
352     {
353     return domain;
354     }
355
356
357     /**
358      * Return the path this cookie is associated with.
359      */

360     public String JavaDoc getPath()
361     {
362     return path;
363     }
364
365
366     /**
367      * Return whether this cookie should only be sent over secure connections.
368      */

369     public boolean isSecure()
370     {
371     return secure;
372     }
373
374
375     /**
376      * @return true if this cookie has expired
377      */

378     public boolean hasExpired()
379     {
380     return (expires != null && expires.getTime() <= System.currentTimeMillis());
381     }
382
383
384     /**
385      * @param req the request to be sent
386      * @return true if this cookie should be sent with the request
387      */

388     protected boolean sendWith(RoRequest req)
389     {
390     HTTPConnection con = req.getConnection();
391     String JavaDoc eff_host = con.getHost();
392     if (eff_host.indexOf('.') == -1) eff_host += ".local";
393
394     return ((domain.charAt(0) == '.' && eff_host.endsWith(domain) ||
395          domain.charAt(0) != '.' && eff_host.equals(domain)) &&
396         Util.getPath(req.getRequestURI()).startsWith(path) &&
397         (!secure || con.getProtocol().equals("https") ||
398          con.getProtocol().equals("shttp")));
399     }
400
401
402     /**
403      * Hash up name, path and domain into new hash.
404      */

405     public int hashCode()
406     {
407     return (name.hashCode() + path.hashCode() + domain.hashCode());
408     }
409
410
411     /**
412      * Two cookies match if the name, path and domain match.
413      */

414     public boolean equals(Object JavaDoc obj)
415     {
416     if ((obj != null) && (obj instanceof Cookie))
417     {
418         Cookie other = (Cookie) obj;
419         return (this.name.equals(other.name) &&
420              this.path.equals(other.path) &&
421              this.domain.equals(other.domain));
422     }
423     return false;
424     }
425
426
427     /**
428      * @return a string suitable for sending in a Cookie header.
429      */

430     protected String JavaDoc toExternalForm()
431     {
432     return name + "=" + value;
433     }
434
435
436     /**
437      * Create a string containing all the cookie fields. The format is that
438      * used in the Set-Cookie header.
439      */

440     public String JavaDoc toString()
441     {
442     String JavaDoc string = name + "=" + value;
443     if (expires != null) string += "; expires=" + expires;
444     if (path != null) string += "; path=" + path;
445     if (domain != null) string += "; domain=" + domain;
446     if (secure) string += "; secure";
447     return string;
448     }
449 }
450
451
Popular Tags