/* * @(#)CookieModule.java 0.3-3 06/05/2001 * * This file is part of the HTTPClient package * Copyright (C) 1996-2001 Ronald Tschalär * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307, USA * * For questions, suggestions, bug-reports, enhancement-requests etc. * I may be contacted at: * * ronald@innovation.ch * * The HTTPClient's home page is located at: * * http://www.innovation.ch/java/HTTPClient/ * */ package HTTPClient; import java.io.File; import java.io.IOException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ProtocolException; import java.util.Vector; import java.util.Hashtable; import java.util.Enumeration; import java.awt.Frame; import java.awt.Panel; import java.awt.Label; import java.awt.Color; import java.awt.Button; import java.awt.Graphics; import java.awt.Dimension; import java.awt.TextArea; import java.awt.TextField; import java.awt.GridLayout; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowAdapter; /** * This module handles Netscape cookies (also called Version 0 cookies) * and Version 1 cookies. Specifically is reads the Set-Cookie * and Set-Cookie2 response headers and sets the Cookie * and Cookie2 headers as neccessary. * *

The accepting and sending of cookies is controlled by a * CookiePolicyHandler. This allows you to fine tune your privacy * preferences. A cookie is only added to the cookie list if the handler * allows it, and a cookie from the cookie list is only sent if the handler * allows it. * *

This module expects to be the only one handling cookies. Specifically, it * will remove any Cookie and Cookie2 header fields found * in the request, and it will remove the Set-Cookie and * Set-Cookie2 header fields in the response (after processing them). * In order to add cookies to a request or to prevent cookies from being sent, * you can use the {@link #addCookie(HTTPClient.Cookie) addCookie} and {@link * #removeCookie(HTTPClient.Cookie) removeCookie} methods to manipulate the * module's list of cookies. * *

A cookie jar can be used to store cookies between sessions. This file is * read when this class is loaded and is written when the application exits; * only cookies from the default context are saved. The name of the file is * controlled by the system property HTTPClient.cookies.jar and * defaults to a system dependent name. The reading and saving of cookies is * enabled by setting the system property HTTPClient.cookies.save * to true. * * @see Netscape's cookie spec * @see HTTP State Management Mechanism spec * @version 0.3-3 06/05/2001 * @author Ronald Tschalär * @since V0.3 */ public class CookieModule implements HTTPClientModule { /** the list of known cookies */ private static Hashtable cookie_cntxt_list = new Hashtable(); /** the file to use for persistent cookie storage */ private static File cookie_jar = null; /** an object, whose finalizer will save the cookies to the jar */ private static Object cookieSaver = null; /** the cookie policy handler */ private static CookiePolicyHandler cookie_handler = new DefaultCookiePolicyHandler(); // read in cookies from disk at startup static { boolean persist; try { persist = Boolean.getBoolean("HTTPClient.cookies.save"); } catch (Exception e) { persist = false; } if (persist) { loadCookies(); // the nearest thing to atexit() I know of... cookieSaver = new Object() { public void finalize() { saveCookies(); } }; try { System.runFinalizersOnExit(true); } catch (Throwable t) { } } } private static void loadCookies() { // The isFile() etc need to be protected by the catch as signed // applets may be allowed to read properties but not do IO try { cookie_jar = new File(getCookieJarName()); if (cookie_jar.isFile() && cookie_jar.canRead()) { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(cookie_jar)); cookie_cntxt_list.put(HTTPConnection.getDefaultContext(), (Hashtable) ois.readObject()); ois.close(); } } catch (Throwable t) { cookie_jar = null; } } private static void saveCookies() { if (cookie_jar != null && (!cookie_jar.exists() || cookie_jar.isFile() && cookie_jar.canWrite())) { Hashtable cookie_list = new Hashtable(); Enumeration enum = Util.getList(cookie_cntxt_list, HTTPConnection.getDefaultContext()) .elements(); // discard cookies which are not to be kept across sessions while (enum.hasMoreElements()) { Cookie cookie = (Cookie) enum.nextElement(); if (!cookie.discard()) cookie_list.put(cookie, cookie); } // save any remaining cookies in jar if (cookie_list.size() > 0) { try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(cookie_jar)); oos.writeObject(cookie_list); oos.close(); } catch (Throwable t) { } } } } private static String getCookieJarName() { String file = null; try { file = System.getProperty("HTTPClient.cookies.jar"); } catch (Exception e) { } if (file == null) { // default to something reasonable String os = System.getProperty("os.name"); if (os.equalsIgnoreCase("Windows 95") || os.equalsIgnoreCase("16-bit Windows") || os.equalsIgnoreCase("Windows")) { file = System.getProperty("java.home") + File.separator + ".httpclient_cookies"; } else if (os.equalsIgnoreCase("Windows NT")) { file = System.getProperty("user.home") + File.separator + ".httpclient_cookies"; } else if (os.equalsIgnoreCase("OS/2")) { file = System.getProperty("user.home") + File.separator + ".httpclient_cookies"; } else if (os.equalsIgnoreCase("Mac OS") || os.equalsIgnoreCase("MacOS")) { file = "System Folder" + File.separator + "Preferences" + File.separator + "HTTPClientCookies"; } else // it's probably U*IX or VMS { file = System.getProperty("user.home") + File.separator + ".httpclient_cookies"; } } return file; } // Constructors CookieModule() { } // Methods /** * Invoked by the HTTPClient. */ public int requestHandler(Request req, Response[] resp) { // First remove any Cookie headers we might have set for a previous // request NVPair[] hdrs = req.getHeaders(); int length = hdrs.length; for (int idx=0; idx 0) { length -= idx-beg; System.arraycopy(hdrs, idx, hdrs, beg, length-beg); } } if (length < hdrs.length) { hdrs = Util.resizeArray(hdrs, length); req.setHeaders(hdrs); } // Now set any new cookie headers Hashtable cookie_list = Util.getList(cookie_cntxt_list, req.getConnection().getContext()); if (cookie_list.size() == 0) return REQ_CONTINUE; // no need to create a lot of objects Vector names = new Vector(); Vector lens = new Vector(); int version = 0; synchronized (cookie_list) { Enumeration list = cookie_list.elements(); Vector remove_list = null; while (list.hasMoreElements()) { Cookie cookie = (Cookie) list.nextElement(); if (cookie.hasExpired()) { Log.write(Log.COOKI, "CookM: cookie has expired and is " + "being removed: " + cookie); if (remove_list == null) remove_list = new Vector(); remove_list.addElement(cookie); continue; } if (cookie.sendWith(req) && (cookie_handler == null || cookie_handler.sendCookie(cookie, req))) { int len = cookie.getPath().length(); int idx; // insert in correct position for (idx=0; idx 0) value.append("$Version=\"" + version + "\"; "); value.append((String) names.elementAt(0)); for (int idx=1; idxCookie.equals()) * already exists in the list then it is replaced with the new cookie. * * @param cookie the Cookie to add * @since V0.3-1 */ public static void addCookie(Cookie cookie) { Hashtable cookie_list = Util.getList(cookie_cntxt_list, HTTPConnection.getDefaultContext()); cookie_list.put(cookie, cookie); } /** * Add the specified cookie to the list of cookies for the specified * context. If a compatible cookie (as defined by * Cookie.equals()) already exists in the list then it is * replaced with the new cookie. * * @param cookie the cookie to add * @param context the context Object. * @since V0.3-1 */ public static void addCookie(Cookie cookie, Object context) { Hashtable cookie_list = Util.getList(cookie_cntxt_list, context); cookie_list.put(cookie, cookie); } /** * Remove the specified cookie from the list of cookies in the default * context. If the cookie is not found in the list then this method does * nothing. * * @param cookie the Cookie to remove * @since V0.3-1 */ public static void removeCookie(Cookie cookie) { Hashtable cookie_list = Util.getList(cookie_cntxt_list, HTTPConnection.getDefaultContext()); cookie_list.remove(cookie); } /** * Remove the specified cookie from the list of cookies for the specified * context. If the cookie is not found in the list then this method does * nothing. * * @param cookie the cookie to remove * @param context the context Object * @since V0.3-1 */ public static void removeCookie(Cookie cookie, Object context) { Hashtable cookie_list = Util.getList(cookie_cntxt_list, context); cookie_list.remove(cookie); } /** * Sets a new cookie policy handler. This handler will be called for each * cookie that a server wishes to set and for each cookie that this * module wishes to send with a request. In either case the handler may * allow or reject the operation. If you wish to blindly accept and send * all cookies then just disable the handler with * CookieModule.setCookiePolicyHandler(null);. * *

At initialization time a default handler is installed. This * handler allows all cookies to be sent. For any cookie that a server * wishes to be set two lists are consulted. If the server matches any * host or domain in the reject list then the cookie is rejected; if * the server matches any host or domain in the accept list then the * cookie is accepted (in that order). If no host or domain match is * found in either of these two lists and user interaction is allowed * then a dialog box is poped up to ask the user whether to accept or * reject the cookie; if user interaction is not allowed the cookie is * accepted. * *

The accept and reject lists in the default handler are initialized * at startup from the two properties * HTTPClient.cookies.hosts.accept and * HTTPClient.cookies.hosts.reject. These properties must * contain a "|" separated list of host and domain names. All names * beginning with a "." are treated as domain names, all others as host * names. An empty string will match all hosts. The two lists are * further expanded if the user chooses one of the "Accept All from * Domain" or "Reject All from Domain" buttons in the dialog box. * *

Note: the default handler does not implement the rules concerning * unverifiable transactions (section 3.3.6, RFC-2965). The reason * for this is simple: the default handler knows nothing about the * application using this client, and it therefore does not have enough * information to determine when a request is verifiable and when not. You * are therefore encouraged to provide your own handler which implements * section 3.3.6 (use the CookiePolicyHandler.sendCookie * method for this). * * @param handler the new policy handler * @return the previous policy handler */ public static synchronized CookiePolicyHandler setCookiePolicyHandler(CookiePolicyHandler handler) { CookiePolicyHandler old = cookie_handler; cookie_handler = handler; return old; } } /** * A simple cookie policy handler. */ class DefaultCookiePolicyHandler implements CookiePolicyHandler { /** a list of all hosts and domains from which to silently accept cookies */ private String[] accept_domains = new String[0]; /** a list of all hosts and domains from which to silently reject cookies */ private String[] reject_domains = new String[0]; /** the query popup */ private BasicCookieBox popup = null; DefaultCookiePolicyHandler() { // have all cookies been accepted or rejected? String list; try { list = System.getProperty("HTTPClient.cookies.hosts.accept"); } catch (Exception e) { list = null; } String[] domains = Util.splitProperty(list); for (int idx=0; idx 0) domain += ".local"; for (int idx=0; idx 0) domain += ".local"; for (int idx=0; idx