1 //
2 // $Id: CookieJar.java 816 2009-06-26 18:49:10Z etienne_sf $
3 //
4 // jupload - A file upload applet.
5 //
6 // Copyright 2007 The JUpload Team
7 //
8 // Created: 08.06.2007
9 // Creator: felfert
10 // Last modified: $Date: 2009-06-26 20:49:10 +0200 (ven., 26 juin 2009) $
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25
26 package wjhk.jupload2.upload.helper;
27
28 import java.net.URL;
29 import java.text.ParseException;
30 import java.text.SimpleDateFormat;
31 import java.util.HashMap;
32 import java.util.StringTokenizer;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35
36 import wjhk.jupload2.policies.UploadPolicy;
37
38 /**
39 * This class implements a container for multiple cookies in a single domain.
40 *
41 * @author felfert
42 */
43 public class CookieJar {
44
45 final static Pattern pNvPair = Pattern.compile(
46 "^\\s*([^=\\s]+)(\\s*=\\s*(.+))*$", Pattern.CASE_INSENSITIVE);
47
48 /**
49 * The current upload policy, always useful.
50 */
51 private UploadPolicy uploadPolicy = null;
52
53 private HashMap<String, Cookie> jar = new HashMap<String, Cookie>();
54
55 private String domain = null;
56
57 private static class Cookie implements Cloneable {
58
59 String domain = null;
60
61 String name = null;
62
63 String value = null;
64
65 String path = null;
66
67 long max_age = 0;
68
69 int version = 0;
70
71 int secure = 0;
72
73 Cookie() {
74 //
75 }
76
77 /**
78 * @throws CloneNotSupportedException
79 * @see java.lang.Object#clone()
80 */
81 @Override
82 public Cookie clone() throws CloneNotSupportedException {
83 Cookie ret = (Cookie) super.clone();
84 ret.domain = this.domain;
85 ret.name = this.name;
86 ret.value = this.value;
87 ret.path = this.path;
88 ret.max_age = this.max_age;
89 ret.version = this.version;
90 ret.secure = this.secure;
91 return ret;
92 }
93
94 /**
95 * Retrieves the hash value of this cookie. Cookies are hashed by name
96 * and path.
97 *
98 * @return The hash value of this cookie.
99 */
100 public String getKey() {
101 String ret = this.name;
102 if (null != this.path)
103 ret += this.path;
104 return ret;
105 }
106
107 /**
108 * Returns a single client cookie header element
109 *
110 * @param path The path of the corresponding request URI
111 * @param secure 1, if the current connection is secure (SSL), 0
112 * otherwise
113 * @return The part of the cookie header or an empty string
114 */
115 public String getHeader(String path, int secure) {
116 StringBuffer sb = new StringBuffer();
117 if ((null == this.path || this.path.equals("/") || this.path
118 .startsWith(path))
119 && (this.secure <= secure)
120 && (this.max_age > System.currentTimeMillis())) {
121 if (this.version > 0) {
122 sb.append("$Version=").append(this.version).append("; ");
123 sb.append(this.name).append("=").append(this.value).append(
124 ";");
125 if (null != this.path)
126 sb.append(" $Path=").append(this.path).append(";");
127 if (null != this.domain)
128 sb.append(" $Domain=").append(this.domain).append(";");
129 } else {
130 sb.append(this.name).append("=").append(this.value).append(
131 ";");
132 }
133 }
134 return sb.toString();
135 }
136
137 }
138
139 /*
140 * **************************************************************************
141 * *************************** Start of CookieJar code
142 * ***********************
143 * ***************************************************
144 * **************************
145 */
146
147 /**
148 * The creator for this class.
149 *
150 * @param uploadPolicy The current upload policy
151 */
152 public CookieJar(UploadPolicy uploadPolicy) {
153 this.uploadPolicy = uploadPolicy;
154 }
155
156 private String stripQuotes(String s) {
157 if (s.startsWith("\"") && s.endsWith("\""))
158 return s.substring(1, s.length() - 1);
159 return s;
160 }
161
162 private boolean domainMatch(String cd) {
163 if (cd == null) {
164 return false;
165 } else {
166 if (!cd.startsWith(".")) {
167 int dot = cd.indexOf('.');
168 if (dot >= 0)
169 cd = cd.substring(dot);
170
171 }
172 return cd.equals(this.domain);
173 }
174 }
175
176 /**
177 * Sets the domain for this cookie jar. If set, only cookies matching the
178 * specified domain are handled.
179 *
180 * @param domain The domain of this instance
181 */
182 public void setDomain(String domain) {
183 if (!domain.startsWith(".")) {
184 int dot = domain.indexOf('.');
185 if (dot >= 0)
186 domain = domain.substring(dot);
187
188 }
189 this.domain = domain;
190 }
191
192 /**
193 * Builds a RFC 2109 compliant client cookie header for the specified URL.
194 *
195 * @param url The URL for which the cookie header is to be used.
196 * @return A client cookie header (including the "Cookie: " prefix) or null
197 * if no cookies are to be set.
198 */
199 public String buildCookieHeader(URL url) {
200 String domain = url.getHost();
201 int dot = domain.indexOf('.');
202 if (dot >= 0)
203 domain = domain.substring(dot);
204 if (domain.equals(this.domain)) {
205 String path = url.getPath();
206 int secure = url.getProtocol().equalsIgnoreCase("https") ? 1 : 0;
207 StringBuffer sb = new StringBuffer();
208 for (String key : this.jar.keySet()) {
209 Cookie c = this.jar.get(key);
210 if (null != c)
211 sb.append(c.getHeader(path, secure));
212 }
213 if (sb.length() > 0) {
214 sb.append("\r\n");
215 return "Cookie: " + sb.toString();
216 }
217 }
218 return null;
219 }
220
221 /**
222 * Parses a "Set-Cookie" header and creates/updates/deletes cookies
223 * according to the parsed values. Parsing is done according to the
224 * specification in RFC 2109
225 *
226 * @param s The plain value of the "Set-Cookie" HTTP header. e.g.: without
227 * the "Set-Cookie: " prefix.
228 */
229 public void parseCookieHeader(String s) {
230 StringTokenizer t = new StringTokenizer(s, ";");
231 Cookie cookie = new Cookie();
232 while (t.hasMoreTokens()) {
233 Matcher m = pNvPair.matcher(t.nextToken());
234 if (m.matches()) {
235 String n = m.group(1);
236 String v = (m.groupCount() > 2 && m.group(3) != null) ? m
237 .group(3).trim() : "";
238 if (n.compareToIgnoreCase("version") == 0) {
239 cookie.version = Integer.parseInt(v);
240 continue;
241 }
242 if (n.compareToIgnoreCase("domain") == 0) {
243 cookie.domain = v;
244 continue;
245 }
246 if (n.compareToIgnoreCase("path") == 0) {
247 cookie.path = v;
248 continue;
249 }
250 if (n.compareToIgnoreCase("max-age") == 0) {
251 cookie.max_age = Integer.parseInt(v);
252 if (cookie.max_age < 0)
253 cookie.max_age = 0;
254 cookie.max_age *= 1000;
255 cookie.max_age += System.currentTimeMillis();
256 continue;
257 }
258 if (n.compareToIgnoreCase("expires") == 0) {
259 SimpleDateFormat df = new SimpleDateFormat(
260 "EEE, dd-MMM-yy HH:mm:ss zzz");
261 try {
262 cookie.max_age = System.currentTimeMillis()
263 - df.parse(v).getTime();
264 if (cookie.max_age < 0)
265 cookie.max_age = 0;
266 } catch (ParseException e) {
267 cookie.max_age = 0;
268 }
269 continue;
270 }
271 if (n.compareToIgnoreCase("comment") == 0) {
272 // ignored
273 continue;
274 }
275 if (n.compareToIgnoreCase("secure") == 0) {
276 cookie.secure = 1;
277 continue;
278 }
279 if (!n.startsWith("$")) {
280 if (null != cookie.name) {
281 if (cookie.version > 0) {
282 // Strip possible quotes
283 cookie.domain = stripQuotes(cookie.domain);
284 cookie.path = stripQuotes(cookie.path);
285 // cookie.comment = stripQuotes(cookie.comment);
286 // cookie.name = stripQuotes(cookie.name);
287 // cookie.value = stripQuotes(cookie.value);
288 }
289 if (domainMatch(cookie.domain)) {
290 if (cookie.max_age > 0) {
291 this.jar.put(cookie.getKey(), cookie);
292 this.uploadPolicy.displayDebug(
293 "[CookieJar] Adding cookie: "
294 + cookie.getKey() + ": "
295 + cookie, 50);
296
297 } else {
298 this.jar.put(cookie.getKey(), null);
299 this.uploadPolicy.displayDebug(
300 "[CookieJar] Ignoring cookie: "
301 + cookie.getKey() + ": "
302 + cookie, 50);
303 }
304 }
305 try {
306 cookie = cookie.clone();
307 } catch (CloneNotSupportedException e) {
308 cookie = new Cookie();
309 }
310 }
311 cookie.name = n;
312 cookie.value = v;
313 }
314 }
315 }
316 }
317 }