// CookieFilter.java
// $Id: CookieFilter.java,v 1.11 2000/08/16 21:38:04 ylafon Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html
package org.w3c.www.protocol.http.cookies;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Writer;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.net.URL;
import org.w3c.util.ObservableProperties;
import org.w3c.www.protocol.http.HttpException;
import org.w3c.www.protocol.http.HttpManager;
import org.w3c.www.protocol.http.PropRequestFilter;
import org.w3c.www.protocol.http.PropRequestFilterException;
import org.w3c.www.protocol.http.Reply;
import org.w3c.www.protocol.http.Request;
import org.w3c.www.http.HttpCookie;
import org.w3c.www.http.HttpCookieList;
import org.w3c.www.http.HttpFactory;
import org.w3c.www.http.HttpMessage;
import org.w3c.www.http.HttpRequestMessage;
import org.w3c.www.http.HttpSetCookie;
import org.w3c.www.http.HttpSetCookieList;
class DomainNode {
Hashtable nodes = null; // < String , DomainNode >
HttpSetCookie cookies [] = null;
int nbcookies = 0;
protected boolean sameCookies(HttpSetCookie c1, HttpSetCookie c2) {
if (c2.getPath() == null && c1.getPath() == null)
return (c1.getName().equals(c2.getName()));
else if (c2.getPath() == null || c1.getPath() == null)
return false;
else return ((c1.getName().equals(c2.getName())) &&
(c1.getPath().equals(c2.getPath())));
}
protected void addCookie(HttpSetCookie cookie) {
int i = 0;
while (i < nbcookies) {
if (sameCookies(cookie,cookies[i])) {
cookies[i] = cookie;
return;
}
i++;
}
if (nbcookies < cookies.length) {
cookies[nbcookies++] = cookie;
} else {
HttpSetCookie ncookies [] = new HttpSetCookie[cookies.length +1];
System.arraycopy(cookies,0,ncookies,0,cookies.length);
ncookies[nbcookies++] = cookie;
cookies = ncookies;
}
}
protected void sync(FileWriter writer)
throws IOException
{
if (nbcookies > 0) {
for (int i = 0; i < nbcookies; i++) {
if (cookies[i].getMaxAge() > 0)
writer.write(DomainTree.cookie2String(cookies[i]));
}
}
Enumeration e = nodes.elements();
DomainNode node = null;
while (e.hasMoreElements()) {
node = (DomainNode)e.nextElement();
node.sync(writer);
}
}
DomainNode() {
nodes = new Hashtable(2);
cookies = new HttpSetCookie[1];
nbcookies = 0;
}
}
class DomainTree {
Hashtable nodes = null; // < String , DomainNode >
protected static String cookie2String(HttpSetCookie cookie) {
Date date = new Date();
return (cookie.getDomain()+ //0
"\t"+
String.valueOf(cookie.getSecurity()).toUpperCase()+ //1
"\t"+
cookie.getPath()+ //2
"\t"+
(cookie.getMaxAge()*1000+date.getTime())+ //3
"\t"+
cookie.getVersion()+ //4
"\t"+
cookie.getName()+ //5
"\t"+
cookie.getValue()+ //6
"\n");
}
protected HttpSetCookie string2Cookie(String cookie[]) {
HttpSetCookie cook = new HttpSetCookie();
cook.setDomain(cookie[0]);
cook.setSecurity(Boolean.getBoolean(cookie[1].toLowerCase()));
cook.setPath(cookie[2]);
long expire = Long.parseLong(cookie[3]);
long now = (new Date()).getTime();
cook.setMaxAge((int) ((expire - now) / 1000));
cook.setVersion(Integer.parseInt(cookie[4]));
cook.setName(cookie[5]);
cook.setValue(cookie[6]);
return cook;
}
protected synchronized void loadCookies(File file)
throws FileNotFoundException
{
try {
FileReader reader = new FileReader(file);
String cookie[] = new String[8];
int i = 0;
int ch;
StringBuffer buffer = new StringBuffer(30);
while ((ch = reader.read()) != -1) {
switch (ch) {
case '#':
while ((ch = reader.read()) != '\n')
if (ch == -1) return;
break;
case '\n':
if (i > 0) {
cookie[i++] = buffer.toString();
HttpSetCookie setcookie = string2Cookie(cookie);
if (setcookie.getMaxAge() > 0)
insertCookie(setcookie);
buffer = new StringBuffer(30);
cookie = new String[8];
i = 0;
}
break;
case '\t':
case ' ':
cookie[i++] = buffer.toString();
buffer = new StringBuffer(30);
break;
default:
buffer.append((char) ch);
}
}
} catch (IOException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
protected synchronized void sync(File file) {
Enumeration e = nodes.elements();
DomainNode node = null;
FileWriter writer = null;
try {
writer = new FileWriter(file);
writer.write("# Jigsaw client HTTP Cookie File\n");
writer.write("# This is a generated file! Do not edit.\n\n");
while (e.hasMoreElements()) {
node = (DomainNode)e.nextElement();
node.sync(writer);
}
writer.close();
} catch (IOException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
} finally {
try {if (writer != null) writer.close();} catch (Exception ex2) {}
}
}
protected boolean isIp(String domain) {
int last = domain.length()-1;
return ((domain.charAt(last)>='0') && (domain.charAt(last)<='9'));
}
protected String[] domainParts(String dom) {
if (dom == null)
return null;
String domain = new String(dom);
Vector V = new Vector(5);
int i = 0; int j = 0; int max = domain.length();
// fix the . symbol bug
while (i < max) {
j = domain.indexOf('.', i);
if (j == -1)
j = max;
V.addElement(domain.substring(i, j));
i = j + 1;
};
// end of fix
if (V.size() == 0)
return null;
String parts [] = new String[V.size()];
V.copyInto(parts);
return parts;
}
protected HttpCookie setCookie2Cookie(HttpSetCookie setcookie) {
HttpCookie cookie = new HttpCookie();
if (setcookie != null) {
cookie.setName(setcookie.getName());
cookie.setValue(setcookie.getValue());
cookie.setDomain(setcookie.getDomain());
cookie.setPath(setcookie.getPath());
cookie.setVersion(setcookie.getVersion());
return cookie;
}
return null;
}
protected void addMatchingPathCookiesInVector(HttpSetCookie cookieArray[],
String path,
Vector V) {
int i = 0;
while ( i < cookieArray.length ) {
if (path.equals("/"))
V.addElement(setCookie2Cookie(cookieArray[i]));
else if (cookieArray[i].getPath() != null) {
if (path.startsWith(cookieArray[i].getPath())) {
// transform SetCookie in Cookie
V.addElement(setCookie2Cookie(cookieArray[i]));
}
}
i++;
}
}
public HttpCookieList getCookies(URL url) {
String domain = url.getHost();
String path = url.getFile();
String parts [] = domainParts(domain);
if (parts == null) //FIXME Exception
return null;
Vector V = new Vector(5);
int i = 0;
DomainNode node = null;
Hashtable childs = nodes;
if (isIp(domain)) {
node = (DomainNode) childs.get(parts[i]);
while ( i < parts.length ) {
if (node == null)
return null;
if (node.nbcookies > 0) {
addMatchingPathCookiesInVector(node.cookies, path, V);
}
if ((i + 1) < parts.length)
node = (DomainNode)childs.get(parts[++i]);
else
node = null;
if (node == null)
break;
childs = node.nodes;
}
} else {
i = parts.length - 1;
node = (DomainNode) childs.get(parts[i]);
while ( i >= 0 ) {
if (node == null)
return null;
if (node.nbcookies > 0) {
addMatchingPathCookiesInVector(node.cookies, path, V);
}
if (i > 0)
node = (DomainNode)childs.get(parts[--i]);
else
node = null;
if (node == null)
break;
childs = node.nodes;
}
}
if (V.size() == 0)
return null;
HttpCookie cookieArray[] = new HttpCookie[V.size()];
V.copyInto(cookieArray);
return HttpFactory.makeCookieList(cookieArray);
}
public void insertCookie(HttpSetCookie cookie) {
String domain = cookie.getDomain();
String parts[] = domainParts(domain);
if (parts == null) //FIXME Exception
return;
int i = 0;
DomainNode node = null;
Hashtable childs = nodes;
if (isIp(domain)) {
node = (DomainNode) childs.get(parts[i]);
while ( true ) {
if (node == null) {
node = new DomainNode();
childs.put(parts[i], node);
}
if (i == parts.length - 1) {
node.addCookie(cookie);
return;
}
node = (DomainNode)childs.get(parts[++i]);
if (node == null) {
node = new DomainNode();
childs.put(parts[i], node);
}
childs = node.nodes;
}
} else {
i = parts.length - 1;
node = (DomainNode) childs.get(parts[i]);
while ( true ) {
if (node == null) {
node = new DomainNode();
childs.put(parts[i], node);
}
if (i == 0) {
node.addCookie(cookie);
return;
}
node = (DomainNode)childs.get(parts[--i]);
if (node == null) {
node = new DomainNode();
childs.put(parts[i], node);
}
childs = node.nodes;
}
}
}
DomainTree() {
this.nodes = new Hashtable(10);
}
}
/**
* Client side CookieFilter :
* @author Benoit Mahe <bmahe@sophia.inria.fr>
*/
public class CookieFilter implements PropRequestFilter {
/**
* The absolute Path of the file use to store cookies.
*/
public static final
String COOKIES_FILE_P = "org.w3c.www.protocol.http.cookie.file";
private static final String defaultFileName = "cookie";
private static DomainTree root = null;
static {
root = new DomainTree();
}
protected HttpManager manager = null;
private static File cookiefile = null;
/**
* The request pre-processing hook.
* Before each request is launched, all filters will be called back
* through
* this method. They will generally set up additional request header
* fields to enhance the request.
* @param request The request that is about to be launched.
* @return An instance of Reply if the filter could handle the request,
* or <strong>null</strong> if processing should continue normally.
* @exception HttpException If the filter is supposed to fulfill the
* request, but some error happened during that processing.
*/
public Reply ingoingFilter(Request request)
throws HttpException
{
HttpCookieList cookielist = root.getCookies(request.getURL());
if (cookielist != null)
request.setCookie(cookielist);
return null;
}
/**
* The request post-processing hook.
* After each request has been replied to by the target server (be it a
* proxy or the actual origin server), each filter's outgoingFilter
* method is called.
* <p>It gets the original request, and the actual reply as a parameter,
* and should return whatever reply it wants the caller to get.
* @param request The original (handled) request.
* @param reply The reply, as emited by the target server, or constructed
* by some other filter.
* @exception HttpException If the reply emitted by the server is not
* a valid HTTP reply.
*/
public Reply outgoingFilter(Request request, Reply reply)
throws HttpException
{
HttpSetCookieList list = reply.getSetCookie();
if (list != null) {
HttpSetCookie [] cooks = list.getSetCookies();
if (cooks != null) {
int i = 0;
while (i < cooks.length) {
if (cooks[i].getDomain() == null)
cooks[i].setDomain(request.getURL().getHost());
root.insertCookie(cooks[i++]);
}
}
}
return reply;
}
/**
* An exception occured while talking to target server.
* This method is triggered by the HttpManager, when the target server
* (which can be a proxy for that request) was not reachable, or some
* network error occured while emitting the request or reading the reply
* headers.
* @param request The request whose processing triggered the exception.
* @param ex The exception that was triggered.
* @return A boolean, <strong>true</strong> if that filter did influence
* the target server used to fulfill the request, and it has fixed the
* problem in such a way that the request should be retried.
*/
public boolean exceptionFilter(Request request, HttpException ex) {
return false;
}
/**
* Synchronized any pending state into stable storage.
* If the filter maintains some in-memory cached state, this method
* should ensure that cached data are saved to stable storage.
*/
public void sync() {
root.sync(cookiefile);
}
/**
* Initialize this filter, using the provided manager.
* During initialization, it is up to the filter to install itself
* in the manager, by invoking the appropriate <code>setFilter</code>
* method.
* @param manager The HttpManager initializing the filter.
* @exception PropRequestFilterException If the filter couldn't be
* initialized properly.
*/
public void initialize(HttpManager manager)
throws PropRequestFilterException
{
this.manager = manager;
ObservableProperties props = manager.getProperties();
String filepath = (String)props.getString(COOKIES_FILE_P, null);
if (filepath == null) {
cookiefile = new File(defaultFileName);
} else {
cookiefile = new File(filepath);
}
if (cookiefile.exists()) {
// load all the Cookie filter and register them in the manager
try {
root.loadCookies(cookiefile);
} catch (FileNotFoundException ex) {
System.out.println(ex.getMessage());
throw new PropRequestFilterException(ex.getMessage());
}
}
manager.setFilter(this);
}
}