/* * CookieSessionHandler.java * * Brazil project web application toolkit, * export version: 2.3 * Copyright (c) 1999-2006 Sun Microsystems, Inc. * * Sun Public License Notice * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is included as the file "license.terms", * and also available at http://www.sun.com/ * * The Original Code is from: * Brazil project web application toolkit release 2.3. * The Initial Developer of the Original Code is: suhler. * Portions created by suhler are Copyright (C) Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): cstevens, drach, suhler. * * Version: 2.2 * Created by suhler on 99/06/28 * Last modified by suhler on 06/11/13 15:02:01 * * Version Histories: * * 2.2 06/11/13-15:02:01 (suhler) * move MatchString to package "util" from "handler" * * 2.1 02/10/01-16:36:38 (suhler) * version change * * 1.19 02/05/10-15:14:14 (suhler) * use util.Guid for cookie name generation * * 1.18 02/05/09-09:53:38 (suhler) * Make invented session-i's more unique * * 1.17 01/08/01-10:16:29 (suhler) * documented "prefix" -> cookie PATH behavior * * 1.16 01/07/31-17:49:47 (drach) * Add a url prefix property * * 1.15 01/07/20-11:30:54 (suhler) * MatchUrl -> MatchString * * 1.14 01/07/20-10:38:10 (suhler) * don't set session if it already exists * * 1.13 01/07/17-14:14:51 (suhler) * use MatchUrl * * 1.12 00/12/11-13:28:46 (suhler) * add class=props for automatic property extraction * * 1.11 00/12/08-16:45:58 (suhler) * doc fixes * * 1.10 00/05/31-13:47:42 (suhler) * doc cleanup * * 1.9 00/04/28-09:22:37 (suhler) * restore "gotCookie" semantics * * 1.8 00/04/24-14:05:57 (cstevens) * Take out hard-coded "SessionID". * * 1.7 00/04/24-12:58:46 (cstevens) * jImport * * 1.6 00/04/20-11:51:15 (cstevens) * Rename SessionHandler to CookieSessionHandler. * * 1.5 00/02/01-19:00:55 (suhler) * strip trailing ";" from cookie name * * 1.4 99/11/30-09:47:01 (suhler) * SessionHandler puts "gotCookie" property in request if a cookie was * retrieved from the browser * * 1.3 99/10/26-18:52:20 (cstevens) * case * * 1.2 99/09/15-14:40:29 (cstevens) * Rewritign http server to make it easier to proxy requests. * * 1.2 99/06/28-11:06:38 (Codemgr) * SunPro Code Manager data about conflicts, renames, etc... * Name history : 2 1 handlers/CookieSessionHandler.java * Name history : 1 0 handlers/SessionHandler.java * * 1.1 99/06/28-11:06:37 (suhler) * date and time created 99/06/28 11:06:37 by suhler * */ package sunlabs.brazil.handler; import sunlabs.brazil.server.Handler; import sunlabs.brazil.server.Request; import sunlabs.brazil.server.Server; import sunlabs.brazil.session.SessionManager; import sunlabs.brazil.util.http.MimeHeaders; import sunlabs.brazil.util.regexp.Regexp; import sunlabs.brazil.util.Guid; import sunlabs.brazil.util.MatchString; import java.util.Hashtable; import java.util.Properties; import java.io.IOException; /** * Handler for creating browser sessions using cookies. * This handler provides a single cookie-id that may be used by * other handlers. * * The intent is to require only one cookie per server. * (See also {@link sunlabs.brazil.filter.SessionFilter}, which * manages sessions with or without cookies). * * The following server properties are used: *
 * If prefix is specified, it is also used to
 * instruct the client to limit the scope of the 
 * browser cookie. to that prefix.
 *
 * 
map
 * ident argument to
 *	{@link SessionManager#getSession} to get the table of valid
 *	cookies, used to map the cookie value to a Session ID.  By default,
 *	the Session ID stored in the request is the cookie value itself.
 * exist
 * SessionManager.
 *	Normally, if the cookie was not present, a new cookie is
 *	automatically created.  
 *
 * session
 * gotCookie is set to the cookie name.  Otherwise it is left unset.
 *
 * @author		Stephen Uhler
 * @version		2.2, 06/11/13
 */
public class CookieSessionHandler
    implements Handler
{
    private static final String COOKIE = "cookie";
    private static final String MAP = "map";
    private static final String EXIST = "exist";
    private static final String PERSIST = "persist"; 
    private static final String PROPERTY = "session";
    private static final String URLPREFIX = "prefix";
    public String cookieName = "cookie";
    public String ident = "";
    public boolean mustExist = false;
    public boolean persist = false;
    public String session = "SessionID";
    MatchString isMine;            // check for matching url
    String urlPrefix;
    String propsPrefix;
    Regexp cookie;
    
    public boolean
    init(Server server, String propsPrefix)
    {
	this.propsPrefix = propsPrefix;
	
	Properties props = server.props;
	isMine = new MatchString(propsPrefix, server.props);
	cookieName = props.getProperty(propsPrefix + COOKIE, cookieName);
	ident = props.getProperty(propsPrefix + MAP, ident);
	mustExist = props.getProperty(propsPrefix + EXIST) != null;
	persist = props.getProperty(propsPrefix + PERSIST) != null;
	session = props.getProperty(propsPrefix + PROPERTY, session);
	urlPrefix = props.getProperty(propsPrefix + URLPREFIX, "/");
	cookie = new Regexp("(^|; *)" + cookieName + "=([^;]*)");
        return true;
    }
    public boolean
    respond(Request request) throws IOException {
	if (!isMine.match(request.url)) {
	    return false;
        }
	String current = request.props.getProperty(session);
	if (current != null && !current.equals("")) {
	    request.log(Server.LOG_INFORMATIONAL, propsPrefix,
		    session + " already exists, skipping");
	    return false;
        }
	/*
	 * Get the cookie out of the http header.  Cookies can be on separate
	 * lines or all combined on one line.  When multiple cookies are on
	 * one line, they are separated by ';' characters.
	 */
	String value = null;
	String[] subs = new String[3];
	MimeHeaders headers = request.headers;
	for (int i = headers.size(); --i >= 0; ) {
	    if (headers.getKey(i).equalsIgnoreCase("Cookie") == false) {
		continue;
	    }
	    if (cookie.match(headers.get(i), subs)) {
		value = subs[2];
		if (value.length() == 0) {
		    value = null;
		}
		break;
	    }
	}
	/* existing session, client can handle sessions */
	if (value != null) {
	    request.props.put("gotCookie", "true");
	}
	/*
	 * If present, map cookie to Session ID.  Otherwise, Session ID is
	 * cookie.
	 */
	String id = value;
	if (id != null) {
	    Hashtable h = (Hashtable) SessionManager.getSession(null, ident,
		    Hashtable.class);
	    id = (String) h.get(id);
	    if (id == null) {
		id = value;
	    }
	}
	/*
	 * Check if Session ID corresponds to an existing session.
	 */
	if ((id != null) && mustExist) {
	    if (SessionManager.getSession(id, null, null) == null) {
		id = null;
	    }
	}
	/*
	 * If Session ID was missing or wasn't known, make a new cookie.
	 */
    
        // System.out.println("must exist " + mustExist);
	if (id != null) {
	    request.log(Server.LOG_INFORMATIONAL, propsPrefix,
		    "Using cookie: " + value);
	} else if (mustExist == false) {
	    String expire = "";
	    if (persist) {
		expire="; EXPIRES=Fri, 01-Jan-25 00:00:00 GMT";
	    }
	    if (value == null) {
		value =  Guid.getString();
		request.log(Server.LOG_INFORMATIONAL, propsPrefix,
			"Creating new cookie: " + value);
		request.addHeader("Set-Cookie", cookieName + "=" + value +
			"; PATH=" + urlPrefix + expire);
	    }
	    id = value;
	}
	if (id != null) {
	    request.props.put(session, id);
	}
	return false;
    }
}