001: // CookieFilter.java
002: // $Id: CookieFilter.java,v 1.11 2000/08/16 21:38:04 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.www.protocol.http.cookies;
007:
008: import java.io.File;
009: import java.io.FileNotFoundException;
010: import java.io.FileReader;
011: import java.io.FileWriter;
012: import java.io.IOException;
013: import java.io.InputStreamReader;
014: import java.io.OutputStreamWriter;
015: import java.io.PrintStream;
016: import java.io.Writer;
017:
018: import java.util.Date;
019: import java.util.Enumeration;
020: import java.util.Hashtable;
021: import java.util.Vector;
022:
023: import java.net.URL;
024:
025: import org.w3c.util.ObservableProperties;
026:
027: import org.w3c.www.protocol.http.HttpException;
028: import org.w3c.www.protocol.http.HttpManager;
029: import org.w3c.www.protocol.http.PropRequestFilter;
030: import org.w3c.www.protocol.http.PropRequestFilterException;
031: import org.w3c.www.protocol.http.Reply;
032: import org.w3c.www.protocol.http.Request;
033:
034: import org.w3c.www.http.HttpCookie;
035: import org.w3c.www.http.HttpCookieList;
036: import org.w3c.www.http.HttpFactory;
037: import org.w3c.www.http.HttpMessage;
038: import org.w3c.www.http.HttpRequestMessage;
039: import org.w3c.www.http.HttpSetCookie;
040: import org.w3c.www.http.HttpSetCookieList;
041:
042: class DomainNode {
043:
044: Hashtable nodes = null; // < String , DomainNode >
045: HttpSetCookie cookies[] = null;
046: int nbcookies = 0;
047:
048: protected boolean sameCookies(HttpSetCookie c1, HttpSetCookie c2) {
049: if (c2.getPath() == null && c1.getPath() == null)
050: return (c1.getName().equals(c2.getName()));
051: else if (c2.getPath() == null || c1.getPath() == null)
052: return false;
053: else
054: return ((c1.getName().equals(c2.getName())) && (c1
055: .getPath().equals(c2.getPath())));
056: }
057:
058: protected void addCookie(HttpSetCookie cookie) {
059: int i = 0;
060: while (i < nbcookies) {
061: if (sameCookies(cookie, cookies[i])) {
062: cookies[i] = cookie;
063: return;
064: }
065: i++;
066: }
067: if (nbcookies < cookies.length) {
068: cookies[nbcookies++] = cookie;
069: } else {
070: HttpSetCookie ncookies[] = new HttpSetCookie[cookies.length + 1];
071: System.arraycopy(cookies, 0, ncookies, 0, cookies.length);
072: ncookies[nbcookies++] = cookie;
073: cookies = ncookies;
074: }
075: }
076:
077: protected void sync(FileWriter writer) throws IOException {
078: if (nbcookies > 0) {
079: for (int i = 0; i < nbcookies; i++) {
080: if (cookies[i].getMaxAge() > 0)
081: writer.write(DomainTree.cookie2String(cookies[i]));
082: }
083: }
084: Enumeration e = nodes.elements();
085: DomainNode node = null;
086: while (e.hasMoreElements()) {
087: node = (DomainNode) e.nextElement();
088: node.sync(writer);
089: }
090: }
091:
092: DomainNode() {
093: nodes = new Hashtable(2);
094: cookies = new HttpSetCookie[1];
095: nbcookies = 0;
096: }
097:
098: }
099:
100: class DomainTree {
101:
102: Hashtable nodes = null; // < String , DomainNode >
103:
104: protected static String cookie2String(HttpSetCookie cookie) {
105: Date date = new Date();
106: return (cookie.getDomain()
107: + //0
108: "\t"
109: + String.valueOf(cookie.getSecurity()).toUpperCase() + //1
110: "\t" + cookie.getPath() + //2
111: "\t" + (cookie.getMaxAge() * 1000 + date.getTime()) + //3
112: "\t" + cookie.getVersion() + //4
113: "\t" + cookie.getName() + //5
114: "\t" + cookie.getValue() + //6
115: "\n");
116: }
117:
118: protected HttpSetCookie string2Cookie(String cookie[]) {
119: HttpSetCookie cook = new HttpSetCookie();
120: cook.setDomain(cookie[0]);
121: cook.setSecurity(Boolean.getBoolean(cookie[1].toLowerCase()));
122: cook.setPath(cookie[2]);
123: long expire = Long.parseLong(cookie[3]);
124: long now = (new Date()).getTime();
125: cook.setMaxAge((int) ((expire - now) / 1000));
126: cook.setVersion(Integer.parseInt(cookie[4]));
127: cook.setName(cookie[5]);
128: cook.setValue(cookie[6]);
129: return cook;
130: }
131:
132: protected synchronized void loadCookies(File file)
133: throws FileNotFoundException {
134: try {
135: FileReader reader = new FileReader(file);
136: String cookie[] = new String[8];
137: int i = 0;
138: int ch;
139: StringBuffer buffer = new StringBuffer(30);
140: while ((ch = reader.read()) != -1) {
141: switch (ch) {
142: case '#':
143: while ((ch = reader.read()) != '\n')
144: if (ch == -1)
145: return;
146: break;
147: case '\n':
148: if (i > 0) {
149: cookie[i++] = buffer.toString();
150: HttpSetCookie setcookie = string2Cookie(cookie);
151: if (setcookie.getMaxAge() > 0)
152: insertCookie(setcookie);
153: buffer = new StringBuffer(30);
154: cookie = new String[8];
155: i = 0;
156: }
157: break;
158: case '\t':
159: case ' ':
160: cookie[i++] = buffer.toString();
161: buffer = new StringBuffer(30);
162: break;
163: default:
164: buffer.append((char) ch);
165: }
166: }
167: } catch (IOException ex) {
168: System.out.println(ex.getMessage());
169: ex.printStackTrace();
170: }
171: }
172:
173: protected synchronized void sync(File file) {
174: Enumeration e = nodes.elements();
175: DomainNode node = null;
176: FileWriter writer = null;
177: try {
178: writer = new FileWriter(file);
179: writer.write("# Jigsaw client HTTP Cookie File\n");
180: writer
181: .write("# This is a generated file! Do not edit.\n\n");
182: while (e.hasMoreElements()) {
183: node = (DomainNode) e.nextElement();
184: node.sync(writer);
185: }
186: writer.close();
187: } catch (IOException ex) {
188: System.out.println(ex.getMessage());
189: ex.printStackTrace();
190: } finally {
191: try {
192: if (writer != null)
193: writer.close();
194: } catch (Exception ex2) {
195: }
196: }
197: }
198:
199: protected boolean isIp(String domain) {
200: int last = domain.length() - 1;
201: return ((domain.charAt(last) >= '0') && (domain.charAt(last) <= '9'));
202: }
203:
204: protected String[] domainParts(String dom) {
205: if (dom == null)
206: return null;
207: String domain = new String(dom);
208: Vector V = new Vector(5);
209: int i = 0;
210: int j = 0;
211: int max = domain.length();
212: // fix the . symbol bug
213: while (i < max) {
214: j = domain.indexOf('.', i);
215: if (j == -1)
216: j = max;
217: V.addElement(domain.substring(i, j));
218: i = j + 1;
219: }
220: ;
221: // end of fix
222: if (V.size() == 0)
223: return null;
224: String parts[] = new String[V.size()];
225: V.copyInto(parts);
226: return parts;
227: }
228:
229: protected HttpCookie setCookie2Cookie(HttpSetCookie setcookie) {
230: HttpCookie cookie = new HttpCookie();
231: if (setcookie != null) {
232: cookie.setName(setcookie.getName());
233: cookie.setValue(setcookie.getValue());
234: cookie.setDomain(setcookie.getDomain());
235: cookie.setPath(setcookie.getPath());
236: cookie.setVersion(setcookie.getVersion());
237: return cookie;
238: }
239: return null;
240: }
241:
242: protected void addMatchingPathCookiesInVector(
243: HttpSetCookie cookieArray[], String path, Vector V) {
244: int i = 0;
245: while (i < cookieArray.length) {
246: if (path.equals("/"))
247: V.addElement(setCookie2Cookie(cookieArray[i]));
248: else if (cookieArray[i].getPath() != null) {
249: if (path.startsWith(cookieArray[i].getPath())) {
250: // transform SetCookie in Cookie
251: V.addElement(setCookie2Cookie(cookieArray[i]));
252: }
253: }
254: i++;
255: }
256: }
257:
258: public HttpCookieList getCookies(URL url) {
259: String domain = url.getHost();
260: String path = url.getFile();
261: String parts[] = domainParts(domain);
262: if (parts == null) //FIXME Exception
263: return null;
264: Vector V = new Vector(5);
265: int i = 0;
266: DomainNode node = null;
267: Hashtable childs = nodes;
268:
269: if (isIp(domain)) {
270: node = (DomainNode) childs.get(parts[i]);
271: while (i < parts.length) {
272: if (node == null)
273: return null;
274: if (node.nbcookies > 0) {
275: addMatchingPathCookiesInVector(node.cookies, path,
276: V);
277: }
278: if ((i + 1) < parts.length)
279: node = (DomainNode) childs.get(parts[++i]);
280: else
281: node = null;
282:
283: if (node == null)
284: break;
285: childs = node.nodes;
286: }
287: } else {
288: i = parts.length - 1;
289: node = (DomainNode) childs.get(parts[i]);
290: while (i >= 0) {
291: if (node == null)
292: return null;
293: if (node.nbcookies > 0) {
294: addMatchingPathCookiesInVector(node.cookies, path,
295: V);
296: }
297: if (i > 0)
298: node = (DomainNode) childs.get(parts[--i]);
299: else
300: node = null;
301: if (node == null)
302: break;
303: childs = node.nodes;
304: }
305: }
306: if (V.size() == 0)
307: return null;
308: HttpCookie cookieArray[] = new HttpCookie[V.size()];
309: V.copyInto(cookieArray);
310: return HttpFactory.makeCookieList(cookieArray);
311: }
312:
313: public void insertCookie(HttpSetCookie cookie) {
314: String domain = cookie.getDomain();
315: String parts[] = domainParts(domain);
316: if (parts == null) //FIXME Exception
317: return;
318: int i = 0;
319: DomainNode node = null;
320: Hashtable childs = nodes;
321: if (isIp(domain)) {
322: node = (DomainNode) childs.get(parts[i]);
323: while (true) {
324: if (node == null) {
325: node = new DomainNode();
326: childs.put(parts[i], node);
327: }
328: if (i == parts.length - 1) {
329: node.addCookie(cookie);
330: return;
331: }
332: node = (DomainNode) childs.get(parts[++i]);
333: if (node == null) {
334: node = new DomainNode();
335: childs.put(parts[i], node);
336: }
337: childs = node.nodes;
338: }
339: } else {
340: i = parts.length - 1;
341: node = (DomainNode) childs.get(parts[i]);
342: while (true) {
343: if (node == null) {
344: node = new DomainNode();
345: childs.put(parts[i], node);
346: }
347: if (i == 0) {
348: node.addCookie(cookie);
349: return;
350: }
351: node = (DomainNode) childs.get(parts[--i]);
352: if (node == null) {
353: node = new DomainNode();
354: childs.put(parts[i], node);
355: }
356: childs = node.nodes;
357: }
358: }
359: }
360:
361: DomainTree() {
362: this .nodes = new Hashtable(10);
363: }
364: }
365:
366: /**
367: * Client side CookieFilter :
368: * @author Benoit Mahe <bmahe@sophia.inria.fr>
369: */
370:
371: public class CookieFilter implements PropRequestFilter {
372:
373: /**
374: * The absolute Path of the file use to store cookies.
375: */
376: public static final String COOKIES_FILE_P = "org.w3c.www.protocol.http.cookie.file";
377:
378: private static final String defaultFileName = "cookie";
379:
380: private static DomainTree root = null;
381:
382: static {
383: root = new DomainTree();
384: }
385:
386: protected HttpManager manager = null;
387:
388: private static File cookiefile = null;
389:
390: /**
391: * The request pre-processing hook.
392: * Before each request is launched, all filters will be called back
393: * through
394: * this method. They will generally set up additional request header
395: * fields to enhance the request.
396: * @param request The request that is about to be launched.
397: * @return An instance of Reply if the filter could handle the request,
398: * or <strong>null</strong> if processing should continue normally.
399: * @exception HttpException If the filter is supposed to fulfill the
400: * request, but some error happened during that processing.
401: */
402: public Reply ingoingFilter(Request request) throws HttpException {
403: HttpCookieList cookielist = root.getCookies(request.getURL());
404: if (cookielist != null)
405: request.setCookie(cookielist);
406: return null;
407: }
408:
409: /**
410: * The request post-processing hook.
411: * After each request has been replied to by the target server (be it a
412: * proxy or the actual origin server), each filter's outgoingFilter
413: * method is called.
414: * <p>It gets the original request, and the actual reply as a parameter,
415: * and should return whatever reply it wants the caller to get.
416: * @param request The original (handled) request.
417: * @param reply The reply, as emited by the target server, or constructed
418: * by some other filter.
419: * @exception HttpException If the reply emitted by the server is not
420: * a valid HTTP reply.
421: */
422: public Reply outgoingFilter(Request request, Reply reply)
423: throws HttpException {
424: HttpSetCookieList list = reply.getSetCookie();
425: if (list != null) {
426: HttpSetCookie[] cooks = list.getSetCookies();
427: if (cooks != null) {
428: int i = 0;
429: while (i < cooks.length) {
430: if (cooks[i].getDomain() == null)
431: cooks[i].setDomain(request.getURL().getHost());
432: root.insertCookie(cooks[i++]);
433: }
434: }
435: }
436: return reply;
437: }
438:
439: /**
440: * An exception occured while talking to target server.
441: * This method is triggered by the HttpManager, when the target server
442: * (which can be a proxy for that request) was not reachable, or some
443: * network error occured while emitting the request or reading the reply
444: * headers.
445: * @param request The request whose processing triggered the exception.
446: * @param ex The exception that was triggered.
447: * @return A boolean, <strong>true</strong> if that filter did influence
448: * the target server used to fulfill the request, and it has fixed the
449: * problem in such a way that the request should be retried.
450: */
451: public boolean exceptionFilter(Request request, HttpException ex) {
452: return false;
453: }
454:
455: /**
456: * Synchronized any pending state into stable storage.
457: * If the filter maintains some in-memory cached state, this method
458: * should ensure that cached data are saved to stable storage.
459: */
460: public void sync() {
461: root.sync(cookiefile);
462: }
463:
464: /**
465: * Initialize this filter, using the provided manager.
466: * During initialization, it is up to the filter to install itself
467: * in the manager, by invoking the appropriate <code>setFilter</code>
468: * method.
469: * @param manager The HttpManager initializing the filter.
470: * @exception PropRequestFilterException If the filter couldn't be
471: * initialized properly.
472: */
473: public void initialize(HttpManager manager)
474: throws PropRequestFilterException {
475: this .manager = manager;
476: ObservableProperties props = manager.getProperties();
477: String filepath = (String) props
478: .getString(COOKIES_FILE_P, null);
479: if (filepath == null) {
480: cookiefile = new File(defaultFileName);
481: } else {
482: cookiefile = new File(filepath);
483: }
484: if (cookiefile.exists()) {
485: // load all the Cookie filter and register them in the manager
486: try {
487: root.loadCookies(cookiefile);
488: } catch (FileNotFoundException ex) {
489: System.out.println(ex.getMessage());
490: throw new PropRequestFilterException(ex.getMessage());
491: }
492: }
493: manager.setFilter(this);
494: }
495: }
|