Bugfixed cookie parsing.
[jsvc.git] / src / dolda / jsvc / util / Cookie.java
1 package dolda.jsvc.util;
2
3 import dolda.jsvc.*;
4 import java.util.*;
5 import java.text.*;
6 import java.io.*;
7
8 public class Cookie {
9     private final static Map<Request, MultiMap<String, Cookie>> cache = new WeakHashMap<Request, MultiMap<String, Cookie>>();
10     public final static DateFormat datefmt;
11     static {
12         datefmt = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.ENGLISH);
13         datefmt.setCalendar(Calendar.getInstance(TimeZone.getTimeZone("UTC")));
14     }
15     public final String name;
16     public String value;
17     public Date expires;
18     public String domain, path;
19     public boolean secure;
20     
21     public Cookie(String name, String value, Date expires, String domain, String path, boolean secure) {
22         if(!Http.istoken(name))
23             throw(new RuntimeException("Invalid cookie name: `" + name + "'"));
24         this.name = name;
25         this.value = value;
26         this.expires = expires;
27         this.domain = domain;
28         this.path = path;
29         this.secure = secure;
30     }
31     
32     public Cookie(String name) {
33         this(name, null, null, null, null, false);
34     }
35     
36     public Cookie(String name, String value) {
37         this(name, value, null, null, null, false);
38     }
39     
40     public String format() {
41         StringBuilder buf = new StringBuilder();
42         buf.append(Http.tokenquote(name));
43         buf.append('=');
44         buf.append(Http.tokenquote(value));
45         if(domain != null)
46             buf.append("; Domain=" + Http.tokenquote(domain));
47         if(path != null)
48             buf.append("; Path=" + Http.tokenquote(path));
49         if(expires != null)
50             buf.append("; Expires=" + Http.tokenquote(datefmt.format(expires)));
51         if(secure)
52             buf.append("; Secure");
53         return(buf.toString());
54     }
55
56     public void addto(Request req) {
57         req.outheaders().add("Set-Cookie", format());
58     }
59     
60     public static MultiMap<String, Cookie> parse(Request req) {
61         MultiMap<String, Cookie> ret = new WrappedMultiMap<String, Cookie>(new TreeMap<String, Collection<Cookie>>());
62         for(String in : req.inheaders().values("Cookie")) {
63             try {
64                 PushbackReader r = new PushbackReader(new StringReader(in));
65                 Cookie c = null;
66                 while(true) {
67                     String k = Http.tokenunquote(r);
68                     Misc.eatws(r);
69                     if((k == null) || (r.read() != '='))
70                         throw(new Http.EncodingException("Illegal cookie header format"));
71                     String v = Http.tokenunquote(r);
72                     if(k.equals("$Version")) {
73                         if(Integer.parseInt(v) != 1)
74                             throw(new Http.EncodingException("Unknown cookie format version"));
75                     } else if(k.equals("$Path")) {
76                         if(c != null)
77                             c.path = v;
78                     } else if(k.equals("$Domain")) {
79                         if(c != null)
80                             c.domain = v;
81                     } else {
82                         c = new Cookie(k, v);
83                         ret.add(k, c);
84                     }
85                     Misc.eatws(r);
86                     int sep = r.read();
87                     if(sep < 0)
88                         break;
89                     if(sep != ';')
90                         throw(new Http.EncodingException("Illegal cookie header format"));
91                 }
92             } catch(IOException e) {
93                 throw(new Error(e));
94             }
95         }
96         return(ret);
97     }
98     
99     public static MultiMap<String, Cookie> get(Request req) {
100         synchronized(cache) {
101             MultiMap<String, Cookie> ret = cache.get(req);
102             if(ret == null) {
103                 ret = parse(req);
104                 cache.put(req, ret);
105             }
106             return(ret);
107         }
108     }
109
110     public String toString() {
111         StringBuilder buf = new StringBuilder();
112         buf.append("Cookie(");
113         buf.append(format());
114         buf.append(")");
115         return(buf.toString());
116     }
117 }