View Javadoc
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 }