head	1.31;
access;
symbols
	TIGRIS_1_1_0RC2:1.13.6.6
	TIGRIS_1_1_0RC1:1.13.6.6
	TIGRIS_1_1:1.13.6.6.0.2
	TIGRIS_1_0_8:1.13.6.6
	TIGRIS_1_0_8RC3:1.13.6.6
	TIGRIS_1_0_8RC2:1.13.6.6
	TIGRIS_1_0_8RC1:1.13.6.6
	TIGRIS_1_0_7:1.13.6.6
	TIGRIS_1_0_7RC3:1.13.6.6
	TIGRIS_1_0_7RC2:1.13.6.5
	TIGRIS_1_0_7RC1:1.13.6.5
	TIGRIS_1_0_6:1.13.6.3
	TIGRIS_1_0_6RC5:1.13.6.3
	TIGRIS_1_0_6RC4:1.13.6.3
	TIGRIS_1_0_6RC3:1.13.6.3
	TIGRIS_1_0_6RC2:1.13.6.2
	TIGRIS_1_0_6RC1:1.13.6.2
	TIGRIS_1_0_5:1.13.6.2
	TIGRIS_1_0_5RC6:1.13.6.2
	TIGRIS_1_0_5RC5:1.13.6.2
	TIGRIS_1_0_5RC4:1.13.6.2
	TIGRIS_1_0_5RC3:1.13.6.2
	TIGRIS_1_0_5RC2:1.13.6.2
	TIGRIS_1_0_5RC1:1.13.6.2
	TIGRIS_1_0_4:1.13.6.2
	TIGRIS_1_0_3:1.13.6.1
	TIGRIS_1_0_2:1.13.6.1
	TIGRIS_1_0_1:1.13
	TIGRIS_1_0:1.13.0.6
	TIGRIS_1_0_0:1.13
	TIGRIS_1_0_0_RC1:1.13.0.2
	dlr:1.1.1
	TIGRIS_0_9_2_4:1.6
	TIGRIS_0_9_2_3:1.6
	HELM_PEER_PORT_BRANCH:1.1.1.2
	TURBINE_PEER:1.8
	TIGRIS_0_9_2_1:1.8
	TIGRIS_0_9_2:1.6.0.2
	TIGRIS_0_9_0:1.4.0.4
	TIGRIS_0_8_4:1.4.0.2
	NEWHELM:1.1.1.1
	TIGRIS_NOV_12_2000:1.1.1;
locks; strict;
comment	@# @;


1.31
date	2001.08.28.17.45.18;	author leonardr;	state Exp;
branches;
next	1.30;

1.30
date	2001.08.28.17.13.33;	author leonardr;	state Exp;
branches;
next	1.29;

1.29
date	2001.08.11.09.08.07;	author dlr;	state Exp;
branches;
next	1.28;

1.28
date	2001.08.09.23.27.08;	author jrobbins;	state Exp;
branches;
next	1.27;

1.27
date	2001.08.08.22.40.46;	author jrobbins;	state Exp;
branches;
next	1.26;

1.26
date	2001.07.29.22.11.43;	author edk;	state Exp;
branches;
next	1.25;

1.25
date	2001.07.29.21.12.58;	author edk;	state Exp;
branches;
next	1.24;

1.24
date	2001.07.19.19.36.29;	author dlr;	state Exp;
branches;
next	1.23;

1.23
date	2001.07.19.19.28.53;	author dlr;	state Exp;
branches;
next	1.22;

1.22
date	2001.07.19.01.04.27;	author dlr;	state Exp;
branches;
next	1.21;

1.21
date	2001.07.19.00.56.24;	author dlr;	state Exp;
branches;
next	1.20;

1.20
date	2001.07.18.10.05.14;	author dlr;	state Exp;
branches;
next	1.19;

1.19
date	2001.07.03.18.19.51;	author stack;	state Exp;
branches;
next	1.18;

1.18
date	2001.06.10.06.05.05;	author stack;	state Exp;
branches;
next	1.17;

1.17
date	2001.05.23.01.50.19;	author dlr;	state Exp;
branches;
next	1.16;

1.16
date	2001.05.04.07.38.47;	author stack;	state Exp;
branches;
next	1.15;

1.15
date	2001.04.30.23.34.33;	author stack;	state Exp;
branches;
next	1.14;

1.14
date	2001.04.15.23.42.12;	author stack;	state Exp;
branches;
next	1.13;

1.13
date	2001.03.23.18.10.44;	author leonardr;	state Exp;
branches
	1.13.6.1;
next	1.12;

1.12
date	2001.02.22.08.37.18;	author dlr;	state Exp;
branches;
next	1.11;

1.11
date	2001.02.22.08.31.28;	author dlr;	state Exp;
branches;
next	1.10;

1.10
date	2001.02.21.01.08.24;	author dlr;	state Exp;
branches;
next	1.9;

1.9
date	2001.02.17.08.37.08;	author stack;	state Exp;
branches;
next	1.8;

1.8
date	2001.01.26.18.29.07;	author edk;	state Exp;
branches
	1.8.2.1;
next	1.7;

1.7
date	2001.01.26.05.24.07;	author edk;	state dead;
branches;
next	1.6;

1.6
date	2001.01.16.06.51.58;	author jrobbins;	state Exp;
branches;
next	1.5;

1.5
date	2001.01.16.04.19.28;	author jrobbins;	state Exp;
branches;
next	1.4;

1.4
date	2000.11.29.12.38.18;	author jrobbins;	state Exp;
branches
	1.4.4.1;
next	1.3;

1.3
date	2000.11.29.00.00.05;	author stack;	state Exp;
branches;
next	1.2;

1.2
date	2000.11.21.01.35.07;	author jon;	state Exp;
branches;
next	1.1;

1.1
date	2000.11.12.21.16.24;	author dlr;	state Exp;
branches
	1.1.1.1;
next	;

1.1.1.1
date	2000.11.12.21.16.24;	author dlr;	state Exp;
branches;
next	1.1.1.2;

1.1.1.2
date	2001.02.21.00.35.07;	author dlr;	state Exp;
branches;
next	;

1.4.4.1
date	2001.01.16.04.23.11;	author jrobbins;	state Exp;
branches;
next	1.4.4.2;

1.4.4.2
date	2001.01.16.06.56.08;	author jrobbins;	state Exp;
branches;
next	;

1.8.2.1
date	2001.01.28.01.02.19;	author dlr;	state dead;
branches;
next	1.8.2.2;

1.8.2.2
date	2001.02.20.04.10.39;	author dlr;	state Exp;
branches;
next	;

1.13.6.1
date	2001.04.24.22.26.48;	author stack;	state Exp;
branches;
next	1.13.6.2;

1.13.6.2
date	2001.05.01.05.45.17;	author stack;	state Exp;
branches;
next	1.13.6.3;

1.13.6.3
date	2001.06.10.05.59.11;	author stack;	state Exp;
branches;
next	1.13.6.4;

1.13.6.4
date	2001.06.21.03.14.44;	author edk;	state Exp;
branches;
next	1.13.6.5;

1.13.6.5
date	2001.06.21.19.52.48;	author dlr;	state Exp;
branches;
next	1.13.6.6;

1.13.6.6
date	2001.07.03.19.28.30;	author stack;	state Exp;
branches;
next	;


desc
@@


1.31
log
@More methods migrated to ContextAPI.
@
text
@/* ================================================================
 * Copyright (c) 2000-2001 CollabNet.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 * any, must include the following acknowlegement: "This product includes
 * software developed by CollabNet <http://www.collab.net/>."
 * Alternately, this acknowlegement may appear in the software itself, if
 * and wherever such third-party acknowlegements normally appear.
 *
 * 4. The hosted project names must not be used to endorse or promote
 * products derived from this software without prior written
 * permission. For written permission, please contact info@@collab.net.
 *
 * 5. Products derived from this software may not use the "Tigris" or
 * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
 * prior written permission of Collab.Net.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of Collab.Net.
 */
package org.tigris.helm.publish;

import java.io.*;
import java.util.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.turbine.util.Log;
import org.apache.commons.util.exception.NestableException;

import org.apache.velocity.context.Context;

import HTTPClient.*;

import org.tigris.helm.security.AccessControlList;
import org.tigris.helm.security.HelmServlet;
import org.tigris.helm.security.RedirectException;
import org.tigris.helm.tool.SessionManager;
import org.tigris.helm.tool.ContextAPI;

/**
 * This class is a <a href="http://noodle.tigris.org">Noodle</a> for Helm.   
 *
 * It subclasses org.tigris.helm.security.HelmSecurity rather than
 * org.tigris.helm.security.BaseHelmServlet as Noodle does 
 * (org.tigris.helm.publish.Noodle).  We cannot subclass Noodle here in Helm 
 * Noodle -- HelmServlet is a subclass of UnsecureServlet -- but we can use 
 * org.tigris.helm.publish.Noodle code herein because most of Noodle is static.
 * <p>
 * This helm Noodle does a permission check on each user access.  When we 
 * call our parent's processRequest method, it will look in our property file 
 * for the 'permission' property.  The parent, HelmServlet, checks that the 
 * current request has permission for this request.  It either lets us through 
 * or throws an ACLException if we're not allowed. Note this scheme allows the 
 * checking of one permission only.  This makes things simple.   Any component
 * that needs an access permission check such as  a CGI should have all
 * accesses go via a sub-class of Noodle w/ the permission to check in the 
 * subclasses properties file (See CvswebContentNoodle for an example).
 * <p>
 *
 * @@author <a href="mailto:stack@@collab.net">St.Ack</a>
 * @@see org.tigris.helm.publish.Noodle
 * @@version $Id: HelmNoodle.java,v 1.30 2001/08/28 17:13:33 leonardr Exp $
 */
public class HelmNoodle extends HelmServlet
{
    /**
     * Properties file to override the properties file in
     * Noodle. This properties file has the NoodleResources 
     * of the org.tigris.helm.publish.NoodleResources original plus the filters added 
     * that depend on this class.
     */
    private static final String PROPS
        = "org.tigris.helm.publish.HelmNoodleResources";

    /**
     * Debug flag.
     */
    private static final boolean DEBUG = true;

    /**
     * Key to put and get uri string into context
     */
    public final static String DATA_KEY = "helmnoodle.data";

    /**
     * Maximum number of cached parsed head tags.
     */
    public final static int MAX_CACHED_HEAD_TAGS = 100;

    /**
     * How long head cache should last, 3 minutes.
     */
    public final static int HEAD_CACHE_TIMEOUT = 180000;

    /**
     * Cache of parsed head tags.
     */
    public static Hashtable cachedHeadTags = null;

    /**
     * Time that cache should be flushed.
     */
    public static long cacheExpiresTime = 0;

    /**
     * The most bytes that we scan when looking for <head> content.
     */
    public final static int MAX_HEAD_BTYES_CONSIDERED = 4000;

    /** 
     * Key to pull binary template from properties file.
     */
    protected final static String BINARY_TEMPLATE_KEY = "binaryTemplate";

    /**
     * Key to use -- from the general zone.properties file -- which allows
     * stripping of html before/after <body ...> and </body> tags.
     */
    protected final static String STRIP_AROUND_BODY =
        "HelmNoodleStripAroundBody";

    /**
     * Key to use -- on a per-servlet basis -- to determine whether the
     * stripping feature (as above, if enabled) should be used for this servlet.
     */
    protected final static String SUBCLASS_STRIP_AROUND_BODY =
        "HelmNoodleSubclassStripAroundBody";

    /** 
     * Servlet start.
     * 
     * This code is duplication of org.tigris.helm.publish.Noodle start. Differences
     * are that we use DEFAULT_PORT and DEFAULT_HOST references AND we've made 
     * adjustment to the default HTTPClient set removing automatic redirect 
     * handling and automatic expansion of gzipped and compressed files (See 
     * <a href="http://www.innovation.ch/java/HTTPClient/advanced_info.html#modules">Advanced Info: Modules</a>). 
     *
     * @@exception ServletException
     */
    public void start() throws ServletException
    {
        super.start();

        // Get a reference to the properties file.
        //
        ResourceBundle rb = null;
        String props = null;
        try
        {
            rb = ResourceBundle.getBundle(PROPS);
            NoodleResources.setBundle(rb);
        }
        catch (Exception e)
        {
            String message 
                = "HelmNoodle: Could not open properties file path: " 
                    + props + " " + e.getMessage();
            System.out.println (message);
            throw new ServletException (message);
        }
        
        // Adjust default HTTPClient module set.
        //
        try
        {
            HTTPConnection.removeDefaultModule(HTTPClient.CookieModule.class);
            HTTPConnection.addDefaultModule
                (Class.forName("org.tigris.helm.publish.NoodleCookieModule"),
                 1);

            HTTPConnection.removeDefaultModule( Class.forName( 
                "HTTPClient.RedirectionModule" ) );
            HTTPConnection.removeDefaultModule( Class.forName( 
                "HTTPClient.ContentEncodingModule" ) );
            HTTPConnection.removeDefaultModule( Class.forName( 
                "HTTPClient.TransferEncodingModule" ) );
        }
        catch (ClassNotFoundException cnfe)
        {
            // Convert the exception.
            //
            throw new ServletException ("HelmNoodle: " + cnfe.getMessage());
        }
        
        // Setup some defaults.
        //
        String port;
        String hostname;
        try 
        {
            port = rb.getString(Noodle.DEFAULT_PORT);
            hostname = rb.getString(Noodle.DEFAULT_HOST);
        } 
        catch (MissingResourceException e) 
        {
            port = myProperties.getProperty("http_port");
            hostname = myProperties.getProperty("hostname");
        }

        if (port == null) port = "80";
        if (hostname == null) hostname = "localhost";

        ProxyModule.setServerPort((new Integer(port)).intValue());
        ProxyModule.setServerName("localhost");
    }

    /**
     * Check we're logged in and save of a noodleData object w/ query string, 
     * etc., if we are not.
     *
     * We do this because we're probably about to cross an internal redirect 
     * that goes via the Login page.  This internal redirect will drop our 
     * needed query string 'page' argument (The internal redirects do not
     * carry over the query string).  This method saves the current 
     * noodleData to context.  Make it protected so it can be called by 
     * subclasses upon entrance.  In particular CHN.  It does stuff before 
     * calling its parent processRequest that can cause the internal redirect 
     * to happen prematurely before this class, the parent, has a chance to 
     * save * off the query string.
     *
     * @@param context WebMacro context
     */
    protected void loggedInCheck( Context context )
    {
        SessionManager sMgr = getSessionManager( context );
        if( !sMgr.isLoggedIn( ).booleanValue( )
            && ( contextGet( context, DATA_KEY ) == null ) )
        {
            contextPut( context, DATA_KEY, makeNoodleData( context ) );
            if( DEBUG )
                Log.debug( "NOT LOGGED IN: contextPut " + DATA_KEY );
        }
    }   

    /**
     * Process the request.
     *
     * If not logged in, we'll be redirected to the login screen by this 
     * classes' parent when we call processRequest.  Save off the current
     * URI, etc. so that when TLogin brings us back here we'll have all our 
     * original info (TLogin drops the query string).  We do this by saving 
     * a HelmNoodleData object into the context.
     * <p>
     * When we come back from TLogin, the browser location string will have 
     * '/issues/TLogin' in it -- not the original request.  Need to fix
     * sometime.  Also, there are issues w/ using a HelmNoodleData that has
     * gone across a TLogin -- it will have 'old' stuff in it... objects from
     * the request before TLogin.  Revisit.
     * <p>
     * Note also that the call to processRequest will  redirect us to an 
     * unauthorized page if we do not have access permissions.  It figures 
     * whether we have permissions by reading a 'permissions' property
     * from this servlets property file.
     *
     * @@param connection Connection to database
     * @@param context WebMacro context
     *
     * @@return The name of this template. NOTE: Returning a templateName of 
     *         null is the means of signaling the caller that the request has
     *         been handled; there is nothing for the caller to do.
     *
     * @@exception HandlerException
     * @@exception RedirectException
     */
    protected String processRequest(Context context)
        throws NestableException, RedirectException
    {        
        if( DEBUG ) Log.debug( "START HelmNoodle" );

        loggedInCheck( context ); // Save query string if we have to log in.

        String templateName = null;

        try
        {
            String page = (String) ContextAPI.getParameter(context, "page");
            templateName = super.processRequest(context );
            computeToolCrumbs(context, page);

            // Checked context for a stashed-away HND. If there, it was put 
            // there by our loggedInCheck method (See above).  Use this 
            // stashed-away noodleData instead w/ a freshened 'context'.
            //
            HelmNoodleData noodleData 
                = ( HelmNoodleData )contextGet( context, DATA_KEY );
            if( noodleData != null )
            {
                noodleData.setContext( context );
                if( DEBUG ) Log.debug( "noodleData pulled from context." );
            }
            else
            {
                noodleData = makeNoodleData(context);
            }

            ProxyModule.proxyRequest( noodleData, "/" );
            HTTPResponse httpResponse = noodleData.getHTTPResponse( );
            String ct = httpResponse.getHeader("Content-Type");

            if( DEBUG )
            {
                int statusCode = httpResponse.getStatusCode( );
                String statusStr = Integer.toString( statusCode );
                Log.debug( "HN response " + statusStr + " " + ct );
            }
            
            if( checkForRedirect( httpResponse, context ) )
            {
                // Setting templateName to null signals to the caller that the
                // request has been completly handled and that the caller is to 
                // do more processing on this request.  In case of a 300-303
                // redirect, checkForRedirect called the BHS redirect method
                // so setting template to null does nothing.  For a 304, we 
                // want to stop any more processing of the request after 
                // setting the result code to 304 and the content length to 
                // null.
                //
                templateName = null; 
                if( DEBUG ) Log.debug( "Noodle redirected." );
            }
            else if (checkForBinary( context, noodleData ) )
            {
                templateName = null;
                if( DEBUG ) Log.debug( "Noodle sent binary data." );
            }
            else
            {
                String responseString 
                    = new String( noodleData.getResponseData( ) );
                context.put( ProxyModule.PAGE_VAR, responseString );
                
                // search for HEAD/TITLE ; if requested, remove them
                // (may modify ProxyModule.PAGE_VAR in the context)
                parseHead(context, ct, responseString, noodleData);

            } // End of if-else that starts w/ check for redirection
        }

        catch (RedirectException re) 
        {
            throw re;
        }

        catch( Throwable e )
        {
            Log.error("internal error in noodle: " + e);
            Log.error(e);
            context.put( ProxyModule.PAGE_VAR, "internal error" );
        }

        finally
        {
            if( DEBUG ) Log.debug( "END HelmNoodle" );
        }
        

        return templateName;
    }

    /**
     * CVS <code>Id</code> token.
     * @@return String name of this servlet.
     */
    public String getServletInfo()
    {
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.30 2001/08/28 17:13:33 leonardr Exp $";
    }

    /** 
     * Send binary data.
     *
     * When we return binary data, we should write binary files directly
     * onto the output * stream.  Passing output through the templating
     * layer will mess it up (by adding a newline to the end) and is more
     * inefficient anyway.
     *
     * @@param inContext Webmacro context.
     * @@param noodleData Noodle data we're to write if we send binary data.
     *
     * @@return true if we decided to send binary data (and actually handled
     * doing so); otherwise false.
     *
     * @@exception java.io.IOException
     */
    private boolean checkForBinary( Context context, NoodleData noodleData )
        throws IOException, HTTPClient.ModuleException
    {
        HTTPResponse httpResponse = noodleData.getHTTPResponse( );
        String ct = httpResponse.getHeader("Content-Type");
        String altTemplate = 
            (String) ContextAPI.getParameter(context, "template");
        String binary =
            (String)ContextAPI.getParameter (context, "binary");
        boolean sendBinaryData = false;

        if (!ct.startsWith("text/html"))
        {
            sendBinaryData = true;
        }
        else if ((altTemplate != null && altTemplate.equals(
                        myProperties.getProperty("binaryTemplate")))
                || binary != null)
        {
            sendBinaryData = true;
        }

        if (DEBUG)
        {
            Log.debug( "checking for binary -- altTemplate: <" + 
                    altTemplate + "> binary flag: <" + binary +
                    "> content-type: <" + ct + "> results: " + sendBinaryData);
        }
        if (sendBinaryData)
        {
            int length = noodleData.getResponseData().length;
            String lengthStr = Integer.toString( length );
            if( DEBUG ) 
                Log.debug( "HN Wrote binary: " + lengthStr + " bytes." );
            HttpServletResponse response = ContextAPI.getResponse(context);
            response.setIntHeader( "Content-Length", length );
            OutputStream out = response.getOutputStream();
            out.write(noodleData.getResponseData());
        }
        return sendBinaryData;
    }

    /**
     * Build up a request URI using passed in request object.
     * @@param inReq  HttpServletRequest object
     */
    protected String getURI( HttpServletRequest inReq )
    {
        StringBuffer uri2Return
            = new StringBuffer( inReq.getServletPath( ) );
        String pathInfo = inReq.getPathInfo( );
        if( pathInfo != null
            && pathInfo.length( ) > 0 )
        {
            uri2Return.append( "/" );
            uri2Return.append( pathInfo );
        }

        String queryStr = inReq.getQueryString( );
        if( queryStr != null
            && queryStr.length( ) > 0 )
        {

            uri2Return.append( "?" );
            uri2Return.append( queryStr );
        }

        return uri2Return.toString( );
    }

    /**
     * Make the noodleData object.
     *
     * @@param context WebMacro context.
     * @@return HelmNoodleData object.
     */
    protected HelmNoodleData makeNoodleData(Context context) 
    {
        HttpServletRequest request = ContextAPI.getRequest(context);
        String method = request.getMethod( );

        HelmNoodleData result 
            = new HelmNoodleData( request, ContextAPI.getResponse(context), 
                                  context );
        try 
        {
            if( method.equalsIgnoreCase( ProxyModule.POST ) )
                result.setPostData( Noodle.postToByteArray( request ) );
        }
        catch( Exception exception )
        {
            Log.error( "IGNORED EXCEPTION doing setPostData: " + exception );
        }

        return result;
    }

    /**
     * Parse data looking for HEAD/TITLE HTML tags.
     * If absent, provide default content.  If found and stripping has been
     * requested, strip everything around &lt;BODY&gt; and &lt;/BODY&gt;.
     * 
     * @@param context WebMacro context.
     * @@param inContentType Content-type of proxy response.
     * @@param responseString Data we got back from proxy request.
     * @@param noodleData Container of proxy response values.
     */    
    protected void parseHead( Context context, String inContentType, 
        String responseString, HelmNoodleData noodleData) 
    {
        if( !inContentType.startsWith( "text/html" ) )
            return;

        int total = responseString.length( );
        int considerLength = ( MAX_HEAD_BTYES_CONSIDERED < total ) 
                    ? MAX_HEAD_BTYES_CONSIDERED : total;
        String topOfHtmlFile 
            = responseString.substring( 0, considerLength ).toLowerCase( );

        final String TITLE_START_TAG = "<title>";
        final String HEAD_START_TAG = "<head>";
        final int HEAD_START_TAG_LENGTH = HEAD_START_TAG.length();
        final String HEAD_END_TAG = "</head>";

        if (cachedHeadTags == null || 
            cachedHeadTags.size() >= MAX_CACHED_HEAD_TAGS || 
            System.currentTimeMillis() > cacheExpiresTime) 
            {
                cachedHeadTags = new Hashtable(MAX_CACHED_HEAD_TAGS);
                cacheExpiresTime = System.currentTimeMillis() +
                    HEAD_CACHE_TIMEOUT;
                if (DEBUG) Log.debug("flushed cachedHeadTags");
            }

        // check for cached content
        String headString = (String) cachedHeadTags.get(noodleData.getURL());

        context.put("headTitle", Boolean.FALSE);
        if (headString == null) {
            int headStart = topOfHtmlFile.indexOf(HEAD_START_TAG);
            if (DEBUG) Log.debug("headStart = " + headStart);
            if (headStart == -1) return;
            int headEnd = topOfHtmlFile.indexOf(HEAD_END_TAG, headStart +
                                                HEAD_START_TAG_LENGTH);
            if (DEBUG) Log.debug("headEnd = " + headEnd);
            if (headEnd == -1) return;
            headString = responseString.substring(headStart +
                                                  HEAD_START_TAG_LENGTH,
                                                  headEnd );
            cachedHeadTags.put(noodleData.getURL(), headString);
        }
        if (DEBUG) Log.debug("headString = " + headString + "|");
        context.put("head", headString);

        if (topOfHtmlFile.indexOf(TITLE_START_TAG) != -1) 
            context.put("headTitle", Boolean.TRUE);

        checkStripAroundBody(context, topOfHtmlFile, responseString,
                considerLength, noodleData);
    }

    /**
     * Check to see if we should strip out everything around the body
     * tags.  This may modify values stored in the context.
     *
     * @@param context WebMacro context.
     * @@param topOfHtmlFile The top part of the HTML file through which to look
     * @@param responseString Data we got back from proxy request.
     * @@param noodleData Container of proxy response values.
     */
    protected void checkStripAroundBody( Context context, String topOfHtmlFile,
            String responseString, int consider, HelmNoodleData noodleData) 
    {
        // has this instance requested stripping around the body?
        String stripFlag =
            myProperties.getProperty(STRIP_AROUND_BODY);
        boolean stripAroundBody = false;
        if (stripFlag != null)
        {
            stripAroundBody = new Boolean(stripFlag).booleanValue();
            stripFlag = null;
        }

        // subclasses of HelmNoodle can say whether nor not stripping
        // up to the <body ...> tag or after the </body> tag is allowed.
        // but there's no need to check if the instance doesn't want it.
        if (stripAroundBody)
        {
            stripFlag =
                myProperties.getProperty(SUBCLASS_STRIP_AROUND_BODY);
            if (stripFlag != null)
            {
                stripAroundBody = new Boolean(stripFlag).booleanValue();
            }
            else
            {
                stripAroundBody = false;
            }
        }
        if (stripAroundBody)
        {
            byte data[] = noodleData.getResponseData();
            // see if we can find the <BODY ...> tag:
            int offsetStart = topOfHtmlFile.indexOf("<body");
            if (offsetStart >= 0)
            {
                offsetStart = topOfHtmlFile.indexOf(">",
                        offsetStart + "<body".length());
            }
    
            // see if we can find the </BODY> tag:
            String bottomOfHtmlFile = new String(
                    data, data.length - consider, consider).toLowerCase();
            int offsetEnd =
                bottomOfHtmlFile.lastIndexOf("</body>");
            if (offsetEnd >= 0 && data.length > consider)
            {
                offsetEnd = data.length - consider + offsetEnd;
            }
    
            // TODO : perhaps we should look harder if we find one but not
            // the other?
            if (offsetStart >= 0 && offsetEnd >= 0)
            {
                // we want the substring starting with offsetStart+1, going to
                // just before offsetEnd
                responseString = new String(
                        data, offsetStart + 1, offsetEnd - offsetStart - 1);
            }
            // store modified version
            context.put (ProxyModule.PAGE_VAR, responseString);
        }
    }
    
    /** 
     * Pass on the redirect.
     *
     * If noodle gets a back a statuscode of 3xx, then pass the
     * redirect back to the browser.  We do this calling
     * org.tigris.helm.security.BaseHelmServlet#redirectToURL.  This
     * in turn calls HttpServletResponse#response which only passes a
     * 302 when our result could have been any of the 3xx codes.  Is
     * this why the HTTPClient does the redirect for you?
     *
     * @@param inHTTPResponse The HTTPClient response.  Holds the result of the
     *        noodle request.
     * @@param inContext Webmacro context. Has everything in it.
     * 
     * @@return Returns true if we're redirecting.
     * 
     * @@exception HandlerException Generic exception used for propogation.
     */
    private boolean checkForRedirect( HTTPResponse inHTTPResponse, 
            Context inContext )
        throws NestableException, RedirectException
    {
        boolean booleanRedirectResult = false;
        try
        {
            int statusCode = inHTTPResponse.getStatusCode( );
            if( statusCode >= HttpServletResponse.SC_MULTIPLE_CHOICES /* 300 */
                && statusCode < HttpServletResponse.SC_NOT_MODIFIED /* 304 */ )
            {
                booleanRedirectResult = true;
                String strStatusCode = Integer.toString( statusCode );
                String location = inHTTPResponse.getHeader( "Location" );
                if( location == null )
                    throw new NestableException( "No location. StatusCode: " 
                        + strStatusCode );
                if( DEBUG ) Log.info( "Redirect to: " + location + 
                    ". StatusCode = " + strStatusCode );
                redirectToURL( inContext, location );
            }
            else if( statusCode == HttpServletResponse.SC_NOT_MODIFIED )
            {
                // 304 needs special handling.
                // See http://www.ics.uci.edu/pub/ietf/http/rfc1945.html#Code304
                // We get a 304 whenever passed an 'If-Modified-Since' header
                // and the data on disk has not changed; server responds w/ a
                // 304 saying I'm not going to send the body because the file 
                // has not changed. 
                //
                booleanRedirectResult = true;
                HttpServletResponse response = ContextAPI.getResponse
                    (inContext);
                response.setIntHeader( "Content-Length", 0 );
                response.setStatus( HttpServletResponse.SC_NOT_MODIFIED );
            }
        }
        
        catch( IOException ioe )
        {
            // Could be io when we have problem w/ socket. Do a conversion to 
            // the HeadlerException
            throw new NestableException("Error checking for redirect", ioe);
        }
        
        catch( HTTPClient.ModuleException moduleException )
        {
            // An internal HTTPClient error.
            throw new NestableException("ModuleException checking for redirect",
                                       moduleException);
        }
        
        return booleanRedirectResult;
    }


    protected void computeToolCrumbs(Context ctx, String page) 
    {
        // subclasses can put a value for $toolCrumbs into the context
    }

}
@


1.30
log
@Completed migration of more methods to ContextAPI.
@
text
@d89 1
a89 1
 * @@version $Id: HelmNoodle.java,v 1.29 2001/08/11 09:08:07 dlr Exp $
d303 1
a303 1
            String page = (String) getParameter(context, "page");
d393 1
a393 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.29 2001/08/11 09:08:07 dlr Exp $";
d417 4
a420 2
        String altTemplate = (String) getParameter(context, "template");
        String binary      = (String) getParameter(context, "binary");
@


1.29
log
@Upgrading to Turbine 3.0-dev, Fulcrum 1.0, and Torque 1.0.
@
text
@d65 1
a65 1

d89 1
a89 1
 * @@version $Id: HelmNoodle.java,v 1.28 2001/08/09 23:27:08 jrobbins Exp $
d393 1
a393 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.28 2001/08/09 23:27:08 jrobbins Exp $";
d444 1
a444 1
            HttpServletResponse response = getResponse(context);
d488 1
a488 1
        HttpServletRequest request = getRequest(context);
d492 2
a493 1
            = new HelmNoodleData( request, getResponse(context), context );
d693 2
a694 1
                HttpServletResponse response = getResponse(inContext);
@


1.28
log
@need to call super before computing breadcrumbs so that currentProject is defined
@
text
@d55 1
a55 1
import org.apache.turbine.util.TurbineException;
d89 1
a89 1
 * @@version $Id: HelmNoodle.java,v 1.27 2001/08/08 22:40:46 jrobbins Exp $
d293 1
a293 1
        throws TurbineException, RedirectException
d393 1
a393 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.27 2001/08/08 22:40:46 jrobbins Exp $";
d663 1
a663 1
        throws TurbineException, RedirectException
d676 1
a676 1
                    throw new TurbineException( "No location. StatusCode: " 
d702 1
a702 1
            throw new TurbineException("Error checking for redirect", ioe);
d708 1
a708 1
            throw new TurbineException("ModuleException checking for redirect",
@


1.27
log
@adding better support for breadcrumbs
@
text
@d89 1
a89 1
 * @@version $Id: HelmNoodle.java,v 1.26 2001/07/29 22:11:43 edk Exp $
d304 1
a305 1
            templateName = super.processRequest(context );
d393 1
a393 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.26 2001/07/29 22:11:43 edk Exp $";
@


1.26
log
@provide support for stripping out everything around <BODY ...> and </BODY>
@
text
@d89 1
a89 1
 * @@version $Id: HelmNoodle.java,v 1.25 2001/07/29 21:12:58 edk Exp $
d303 2
d382 1
d393 1
a393 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.25 2001/07/29 21:12:58 edk Exp $";
d714 7
@


1.25
log
@clean up internal logic:
* don't make string copies when it's unnecessary (specifically when we
  send binary content)
* put all the logic related to figuring out if we're sending binary content
  in one place
* allow specification of whether or not we're sending binary content by
  providing a flag (binary={anything}) rather than using a magic template
  name
@
text
@d89 1
a89 1
 * @@version $Id: HelmNoodle.java,v 1.24 2001/07/19 19:36:29 dlr Exp $
d105 1
a105 1
    private static final boolean DEBUG = false;
d142 14
d356 4
a360 1
                parseHeadForTitle(context, ct, responseString, noodleData);
d390 1
a390 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.24 2001/07/19 19:36:29 dlr Exp $";
d504 3
a506 2
     * Parse data looking for HTML &lt;TITLE>.
     * If absent, add one.
d513 1
a513 1
    protected void parseHeadForTitle( Context context, String inContentType, 
d520 1
a520 1
        int len = ( MAX_HEAD_BTYES_CONSIDERED < total ) 
d523 1
a523 1
            = responseString.substring( 0, len ).toLowerCase( );
d562 76
@


1.24
log
@Removed Joist references.
@
text
@d89 1
a89 1
 * @@version $Id: HelmNoodle.java,v 1.23 2001/07/19 19:28:53 dlr Exp $
d310 1
a310 1
            String altTemplate = (String) getParameter(context, "template");
d315 1
a315 3
                Log.debug( "HN response " + statusStr 
                    + " " + ct + " altTemplate <" 
                    + altTemplate + ">." );
d332 5
d343 1
a343 16
                if (!ct.startsWith("text/html") || altTemplate != null )
                {
                    if (altTemplate == null) 
                       altTemplate = myProperties.getProperty("binaryTemplate");

                    if (DEBUG) Log.debug("old TEMPLATE: " + templateName );
                    templateName 
                        = myProperties.getProperty("SERVLET_TEMPLATE_ROOT") 
                            + "/" + altTemplate;
                    if (DEBUG) Log.debug("new TEMPLATE: " + templateName );
                    
                    templateName 
                        = checkForBinary( context, templateName, noodleData );
                }

                parseHeadForTitle( context, ct, responseString, noodleData );
d354 3
a356 1
           context.put( ProxyModule.PAGE_VAR, Noodle.stackTrace( e ) );
d373 1
a373 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.23 2001/07/19 19:28:53 dlr Exp $";
d377 6
a382 7
     * Check for return of binary data.
     * 
     * If returning binary data, write binary files directly onto the output 
     * stream.  Do not pass binary output to WebMacro for outputting; it messes 
     * it up (Adds a newline to the end).  Pass back a null template name as 
     * signal to the parent that the request has been handled and written back 
     * to the client.
d385 4
a388 2
     * @@param inTemplateName Name of template.
     * @@param inNoodleData Noodle data we're to write if binary data.
a389 4
     * @@return Templatename to return.  We set it to null if we handle the 
     * binary return -- a null templateName is flag to the caller that the 
     * request has been handled in here and caller does not have to do anything.
     * 
d392 2
a393 3
    private String checkForBinary( Context inContext, 
            String inTemplateName, NoodleData inNoodleData )
        throws IOException
d395 5
a399 1
        String templateName = inTemplateName;
d401 1
a401 2
        if( templateName.endsWith( 
            myProperties.getProperty( BINARY_TEMPLATE_KEY ) ) )
d403 18
a420 1
            int length = inNoodleData.getResponseData().length;
d424 1
a424 1
            HttpServletResponse response = getResponse(inContext);
d427 1
a427 2
            out.write(inNoodleData.getResponseData());
            templateName = null;
d429 1
a429 2

        return templateName;  
@


1.23
log
@Removed Joist references.
@
text
@d71 2
a72 2
 * org.joist.security.UnsecureServlet as joist Noodle does 
 * (org.joist.publish.Noodle).  We cannot subclass joist Noodle here in helm 
d74 1
a74 1
 * org.joist.publish.Noodle code herein because most of joist Noodle is static.
d88 2
a89 2
 * @@see org.joist.publish.Noodle
 * @@version $Id: HelmNoodle.java,v 1.22 2001/07/19 01:04:27 dlr Exp $
d145 1
a145 1
     * This code is duplication of org.joist.publish.Noodle start. Differences
d226 1
a226 1
     * needed query string 'page' argument (The joist internal redirects do not
d325 1
a325 1
                // redirect, checkForRedirect called the joist redirect method
d383 1
a383 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.22 2001/07/19 01:04:27 dlr Exp $";
@


1.22
log
@Removed Joist dependancies.
@
text
@d89 1
a89 1
 * @@version $Id: HelmNoodle.java,v 1.21 2001/07/19 00:56:24 dlr Exp $
d96 1
a96 1
     * of the org.joist.publish.NoodleResources original plus the filters added 
d383 1
a383 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.21 2001/07/19 00:56:24 dlr Exp $";
@


1.21
log
@NoodleCookieModule has been ported from Joist to Helm.
@
text
@a60 2
import org.joist.publish.*;

d89 1
a89 1
 * @@version $Id: HelmNoodle.java,v 1.20 2001/07/18 10:05:14 dlr Exp $
d383 1
a383 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.20 2001/07/18 10:05:14 dlr Exp $";
d545 6
a550 6
     * If noodle gets a back a statuscode of 3xx, then pass the redirect back 
     * to the browser.  We do this calling
     * org.joist.security.UnsecureServlet#aredirectToURL.  This in turn calls
     * HttpServletResponse#response which only passes a 302 when our result 
     * could have been any of the 3xx codes.  Is this why the HTTPClient does
     * the redirect for you?
d558 1
a558 1
     * @@exception HandlerException  Generic exception used for propogation.
@


1.20
log
@Ported Joist Redirect*Exception to Helm.
@
text
@d91 1
a91 1
 * @@version $Id: HelmNoodle.java,v 1.19 2001/07/03 18:19:51 stack Exp $
d182 3
a184 2
            HTTPConnection.addDefaultModule(
                Class.forName("org.joist.publish.NoodleCookieModule"), 1);
d385 1
a385 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.19 2001/07/03 18:19:51 stack Exp $";
@


1.19
log
@Remove ContentEncoding and TransferEncoding modules.  These modules do
automatic expansion of gzipped and compressed files.  Meant that any such
file passed via noodle was automatically expanded upon download.  Probably
not what we want. This behavior is especially problematic when the file
being expanded is large. As is, our JVM has upper limit of 32megs on a block
allocation.  A gzipped file of 7megs expanded in memory was causing OOME
exceptions.  As is, noodle has upper bound of whatever the largest block
the JVM will allocate.  To be revisited.

Some clean up.
@
text
@a53 2
import java.sql.*;

a55 1
import org.apache.turbine.util.TurbineException;
a58 2
import org.joist.security.*;

a60 1
import org.tigris.helm.tool.SessionManager;
d63 1
d65 2
a66 1
import org.tigris.helm.security.AccessControlList;
d91 1
a91 1
 * @@version $Id: HelmNoodle.java,v 1.18 2001/06/10 06:05:05 stack Exp $
d384 1
a384 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.18 2001/06/10 06:05:05 stack Exp $";
@


1.18
log
@HelmNoodleparses first 4k of file it picks from disk looking for HTML
HEAD tags.  If it finds them, it puts the string that is between HEAD
tags into a context variable 'head'.

Currently HN cuts a character too much from the end of this string.
This commit fixes that.  Cutting a character too much is a problem
if there are tags abutted against the close HEAD tag.
@
text
@d95 1
a95 1
 * @@version $Id: HelmNoodle.java,v 1.17 2001/05/23 01:50:19 dlr Exp $
d151 5
a155 10
     * This code is duplication of org.joist.publish.Noodle start method.  No 
     * changes have been made to this method other than DEFAULT_PORT and 
     * DEFAULT_HOST references AND we've made it so the redirect module in the 
     * HTTPClient lib is not automatically loaded -- let redirects go back to
     * the browser. 
     * <p>
     * I have to duplicate the code because i cannot subclass or make an 
     * Adapter [GoF95] that creates its own instance of Noodle (Classes cannot 
     * instantiate servlets -- only the servlet runner can instantiate 
     * servlets).
d163 2
a164 1
        // get a reference to the properties file.
d181 1
a181 2
        // Remove the default HTTPClient.CookieModule.class
        // so that it can be overridden by our own implementation
a182 1
        HTTPConnection.removeDefaultModule(HTTPClient.CookieModule.class);
d185 1
d188 7
d198 1
a198 1
            // should never get here, but just in case
d203 1
a203 2
        // Remove the automatic handling of redirects module too.  Let redirects
        // go back to the browser.
a204 14
        try
        {
            HTTPConnection.removeDefaultModule( Class.forName( 
                "HTTPClient.RedirectionModule" ) );
        }
        catch( ClassNotFoundException classnotfoundexception )
        {
            // Convert the exception.
            //
            throw new ServletException ("HelmNoodle: " 
                + classnotfoundexception.getMessage());
        }

        // setup some defaults
a211 1

a221 1
        //ProxyModule.setServerName(hostname);
d388 1
a388 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.17 2001/05/23 01:50:19 dlr Exp $";
@


1.17
log
@Ported Helm from WebMacro to Velocity.
@
text
@d95 1
a95 1
 * @@version $Id: HelmNoodle.java,v 1.16 2001/05/04 07:38:47 stack Exp $
d403 1
a403 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.16 2001/05/04 07:38:47 stack Exp $";
d552 1
a552 1
                                                  headEnd - 1);
@


1.16
log
@Spring-cleaning.  Add in fix for Binary data handling that was in
the TIGRIS_1_0 branch but not here in the head (IZ3410).  Added
handling of 304s.  Cleaned javadoc.  Put related logic together
either into methods or into the one logic block.
@
text
@d57 2
d60 1
a60 1
import org.webmacro.servlet.*;
d66 1
a66 1
import org.joist.session.*;
d95 1
a95 1
 * @@version $Id: HelmNoodle.java,v 1.15 2001/04/30 23:34:33 stack Exp $
d256 1
a256 1
    protected void loggedInCheck( WebContext context )
d298 2
a299 2
    protected String processRequest( Connection connection, WebContext context )
        throws HandlerException, RedirectException
d309 1
a309 1
            templateName = super.processRequest( connection, context );
d330 1
a330 1
            String altTemplate = (String) context.getForm("template");
d403 1
a403 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.15 2001/04/30 23:34:33 stack Exp $";
d425 1
a425 1
    private String checkForBinary( WebContext inContext, 
d438 1
a438 1
            HttpServletResponse response = inContext.getResponse( );
d482 1
a482 1
    protected HelmNoodleData makeNoodleData(WebContext context) 
d484 1
a484 1
        HttpServletRequest request = context.getRequest( );
d488 1
a488 1
            = new HelmNoodleData( request, context.getResponse( ), context );
d511 1
a511 1
    protected void parseHeadForTitle( WebContext context, String inContentType, 
d533 2
a534 1
                cacheExpiresTime = System.currentTimeMillis() + HEAD_CACHE_TIMEOUT;
d546 2
a547 1
            int headEnd = topOfHtmlFile.indexOf(HEAD_END_TAG, headStart+HEAD_START_TAG_LENGTH);
d550 3
a552 2
            headString =  responseString.substring(headStart+HEAD_START_TAG_LENGTH,
                                                          headEnd - 1);
d581 2
a582 2
            WebContext inContext )
        throws HandlerException, RedirectException
d595 1
a595 1
                    throw new HandlerException( "No location. StatusCode: " 
d611 1
a611 1
                HttpServletResponse response = inContext.getResponse( );
d617 1
a617 1
        catch( IOException ioexception )
d621 1
a621 3
            //
            throw new HandlerException( "IOException checking for redirect: "
                + ioexception.toString( ) );
d626 3
a628 5
            // An internal HTTPClient error. Do a conversion to  the 
            // HendlerException
            //
            throw new HandlerException( "ModuleException checking 4 redirect: "
                + moduleException.toString( ) );
@


1.15
log
@CHN was preempting HNs saving of the query string across the joist
internal redirect (Joist internal redirect drops the query string).
Broke out the query string saving as a method for CHN to call on
entrance.
@
text
@a45 1

d72 17
a88 6
 * This class is Noodle only it subclasses
 * org.tigris.helm.security.HelmSecurity rather than
 * org.joist.security.UnsecureServlet as joist Noodle does.  We cannot do a 
 * true subclass -- HelmServlet is a subclass of UnsecureServlet -- but this 
 * class reuses most of Noodle. This is possible because most 
 * of Noodle is static.
d90 1
a90 19
 * This 'override' is done to add the WebMacro context to NoodleData so that it 
 * is available to Noodle filters. The subclassing of HelmServlet is so we can 
 * implement permission checking on the user request.  When we call our parent's 
 * processRequest method, it will look in our property file for the 'permission' 
 * property.  The parent, HelmServlet, checks that the current request has 
 * permission for this request.  It either lets us through or throws an 
 * ACLException if we're not allowed. Note this scheme allows the checking of 
 * one permission only.  This makes things simple. 
 * </p>
 * I've turned off the automatic handling of redirects by the HTTPClient
 * library.  We came across it when trying to wrap the php calendar program w/
 * Noodle. It was messing us up.  I thought about having a properties file 
 * where you could turn this feature on and off as you needed it only the 
 * actual HTTPClient connection is done down in ProxyModule.
 * In here is where you'd want to know if the feature was wanted or not.  I 
 * would have to disturb old code.  I'll wait till someone needs such a
 * feature.  See 
 * <a href="http://www.innovation.ch/java/HTTPClient/advanced_info.html#modules">Advanced HTTPClient Info.: Modules</a>.
 * 
d92 2
a93 3
 * @@see Noodle
 * @@version $Id: HelmNoodle.java,v 1.14 2001/04/15 23:42:12 stack Exp $
 * @@see <a href="http://projects.collab.net/project/eng/stack/projects/securenoodle/">SecureNoodle: Problem Statement, Requirements, Survey, Decisions and Testing</a>
a97 6
     * Class version string
     */
    private static final String versionID
        = "$Id: HelmNoodle.java,v 1.14 2001/04/15 23:42:12 stack Exp $";

    /**
d100 2
a101 1
     * of the original plus the filters added that depend on this class.
d114 1
a114 2
    public final static String DATA_KEY
        = "helmnoodle.data";
d142 5
d149 5
a153 6
     * This code is duplication of Noodle
     * start method.  No changes have been made to this method
     * other than DEFAULT_PORT and DEFAULT_HOST references AND we've made it so
     * the redirect module is not automatically loaded (Was causing problems 
     * w/ the calendar application).  See above in the class javadoc for 
     * comment.
d155 5
a159 7
     * I have to duplicate the code because i cannot subclass or make an Adapter 
     * [GoF95] that creates its own instance of Noodle (You 
     * cannot nstantiate servlets -- only the servlet runner can).
     * <p>
     * Runtime dependancy on <code>org.joist.publish.NoodleCookieModule</code>
     * and <code>HTTPClient.RedirectionModule</code>.
     * 
d178 1
a178 3
                    + props 
                    + " " 
                    + e.getMessage();
d199 4
a202 3
        // Remove the automatic handling of redirects module too.
	    //
	    try
d218 2
a219 1
        try {
d222 4
a225 1
        } catch (MissingResourceException e) {
d229 1
d239 1
a239 1
     * Check we're logged in and save off a noodleData object w/ query string, 
d243 3
a245 2
     * that goes by the Login page.  This internal redirect will drop our 
     * needed query string 'page' argument.  This method saves the current 
d247 4
a250 2
     * CHN.  It does stuff before calling processRequest that can cause the 
     * internal redirect to happen prematurely.
a268 6
     * This method is method is very like
     * Noodle.processRequest. It's an 'override'.
     * The difference is that it makes a HelmNoodleData class in place of a 
     * NoodleData class. HelmNoodleData has a webmacro 
     * context where NoodleData does not.
     * <p>
d270 2
a271 2
     * classes' parent whenwe call processRequest.  Save off the current
     * URI, etc. so that when TLogin brings us back to here we'll have all our 
d284 1
a284 1
     * from this servlets property file
d289 3
a291 1
     * @@return The name of this template (?)
d296 2
a297 4
    protected String processRequest( Connection connection,
                                        WebContext context )
        throws HandlerException,
                RedirectException
d299 1
a299 2
        if( DEBUG )
            Log.debug( "Begin" );
d301 1
a301 14
     
        // Build up the NoodleData. Do it before processRequest in case we're 
        // not logged in.  This way we can save off the request while we're 
        // redirected to TLogin.  This technique is problematic because it is
        // preserving old data.  I should be more selective about what it is 
        // that i want to preserve.  I'm uncertain as to what should be saved
        // at time of writing (11/28/2000) mostly because i'm unsure of the 
        // TLogin redirect mechanism -- what it does and doesn't preserve
        // (query strings, POST data, etc.).  During the writing of this code
        // WebMacro was upgraded adding to the uncertainty (It broke context
        // handling across TLogin -- edk added contextGet to fix).
        //
        HelmNoodleData noodleData = makeNoodleData(context);
        String method = context.getRequest( ).getMethod( );
a302 16
        try
        {
            if( method.equalsIgnoreCase( ProxyModule.POST ) )
            {
                noodleData.setPostData(
                    Noodle.postToByteArray(
                    context.getRequest( ) ) );
            }
        }
        catch (Exception e)
        {
            // ignore
        }
        
        loggedInCheck( context );
        
d308 6
a313 2
            if (DEBUG) Log.info("old TEMPLATE: " + templateName );
            HelmNoodleData isNoodleData 
d315 1
a315 1
            if( isNoodleData != null )
a316 10
                // If something in the context, it was put there because we 
                // weren't logged in.  Because we weren't logged in, we were 
                // redirected to TLogin.  TLogin then bounced us back here.
                // On entry this time, we ARE logged in so we by-pass putting 
                // a new object into context   overwriting the object we'd put 
                // there before the TLogin redirect.  Pull the object we 
                // stashed away and use it.  'Freshen' the context it has. 
                // It's context is pre-TLogin unless i freshen it.
                //
                noodleData = isNoodleData;
d318 5
a322 2
                if( DEBUG )
                    Log.debug( "contextGet " + DATA_KEY );
d324 1
a324 2
            
            // Do the proxying.
a325 1

d327 10
d338 1
a338 1
            if( ! checkForRedirect( httpResponse, context ) )
d340 19
a358 5
                String ct = httpResponse.getHeader("Content-Type");
                if (DEBUG) Log.info("content type: " + ct );
                String altTemplate = (String) context.getForm("template");
                if (DEBUG) Log.info("altTemplate: " + altTemplate );
                if (!ct.startsWith("text/html") || altTemplate != null)
d362 1
d364 3
a366 3
                    templateName =
                       myProperties.getProperty("SERVLET_TEMPLATE_ROOT") + "/" +
                       altTemplate;
d368 3
d372 3
a374 13
    
                String responseString = new String( noodleData.getResponseData( ) );
                context.put( ProxyModule.PAGE_VAR, responseString);
                if (ct.startsWith("text/html"))
                {
                    int consider =
                       MAX_HEAD_BTYES_CONSIDERED < noodleData.getResponseData().length ?
                       MAX_HEAD_BTYES_CONSIDERED : noodleData.getResponseData().length;
                    String topOfHtmlFile = (new
                       String(noodleData.getResponseData(), 0, consider)).toLowerCase();
                    parseHead(context, topOfHtmlFile, responseString, noodleData);
                }
            } // End of check for exception
d377 2
a378 1
        catch (RedirectException re) {
d384 1
a384 2
           context.put( ProxyModule.PAGE_VAR,
                Noodle.stackTrace( e ) );
d389 1
a389 2
            if( DEBUG )
                Log.debug( "End" );
a394 1

d401 1
a401 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.14 2001/04/15 23:42:12 stack Exp $";
d404 41
d474 6
d482 3
d486 11
a496 3
            = new HelmNoodleData( context.getRequest( ),
                context.getResponse( ),
                context );
d500 11
a510 2
    protected void parseHead(WebContext context, String topOfHtmlFile,
                             String responseString, HelmNoodleData noodleData) 
d512 9
d576 2
a577 2
        WebContext inContext )
            throws HandlerException, RedirectException
d583 2
a584 1
            if( statusCode >= 300 && statusCode < 304 )
d596 1
a596 1
            else if( statusCode == 304 )
d600 4
a603 2
                // TODO: Look into this more.  The below stops an exception 
                // but is probably not doing the right thing.
d607 2
a608 1
                response.setStatus( 304 );
d626 1
a626 1
            throw new HandlerException( "ModuleException checking for redirect: "
@


1.14
log
@Make Noodle deal w/ 304 responses.  Needs more work.
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.13 2001/03/23 18:10:44 leonardr Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.13 2001/03/23 18:10:44 leonardr Exp $";
d247 24
d342 2
a343 12

        // Save request into the context if we're not logged in.
        // It gets lost when we're bounced to TLogin if we don't.
        //
        SessionManager sMgr = getSessionManager( context );
        String savedQuery = null;
        if( !( sMgr.isLoggedIn( ) ).booleanValue( ) )
        {
            contextPut( context, DATA_KEY, noodleData );
            if( DEBUG )
                Log.info( "NOT LOGGED IN: contextPut " + DATA_KEY );
        }
d371 1
a371 1
            ProxyModule.proxyRequest( noodleData );
d432 1
a432 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.13 2001/03/23 18:10:44 leonardr Exp $";
@


1.13
log
@Turned off debuggin.
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.12 2001/02/22 08:37:18 dlr Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.12 2001/02/22 08:37:18 dlr Exp $";
d418 1
a418 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.12 2001/02/22 08:37:18 dlr Exp $";
d524 1
a524 1
            if( statusCode >= 300 && statusCode < 400 )
d536 11
d552 1
a552 1
            // the HendlerException
@


1.13.6.1
log
@WebMacro adds a newline.  Was messing us up when we were trying to write
the client pure binary data.  Edk suggested by-passing WebMacro write
directly to the client.  Pass UnsecureServlet an empty template name
to flag it that the request has been handled (IZ3410).

Reviewed by: Commit reviewed/buddied by edk.
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.13 2001/03/23 18:10:44 leonardr Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.13 2001/03/23 18:10:44 leonardr Exp $";
a156 6
     * Key to pull binary template from properties file.
     */
    protected final static String BINARY_TEMPLATE_KEY = "binaryTemplate";


    /** 
d360 1
a360 3

            addResponseHeaders( context.getResponse( ) );

d370 1
a370 1
                       altTemplate = myProperties.getProperty(BINARY_TEMPLATE_KEY);
a379 1

a388 15
                else if( templateName.endsWith(
                    myProperties.getProperty(BINARY_TEMPLATE_KEY)))
                {
                    // Write binary files directly onto the output stream.
                    // Do not pass binary output to WebMacro for outputting;
                    // it messes it up (Adds a newline to the end.  
                    // Pass back a null template name as signal to 
                    // the parent that the request has been handled and
                    // written back to the client.
                    //
                    // should probably set Content-Length, since we know it
                    OutputStream out = context.getResponse().getOutputStream();
                    out.write(noodleData.getResponseData());
                    templateName = null;
                }
a410 9
    /** 
     * Add headers to the response.
     * For overriding classes to add headers to the response.
     *
     * @@param inResponse The HTTP response.  Add header to this. 
     */
    protected void addResponseHeaders( HttpServletResponse inResponse )
    {
    }
d418 1
a418 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.13 2001/03/23 18:10:44 leonardr Exp $";
d524 1
a524 1
            if( statusCode >= 300 && statusCode < 304 )
a534 11
            }
            else if( statusCode == 304 )
            {
                // 304 needs special handling.
                // See http://www.ics.uci.edu/pub/ietf/http/rfc1945.html#Code304
                // TODO: Look into this more.  The below stops an exception 
                // but is probably not doing the right thing.
                //
                booleanRedirectResult = true;
                HttpServletResponse response = inContext.getResponse( );
                response.setStatus( 304 );
@


1.13.6.2
log
@CHN was preempting its parents' saving of query string.  This code breaks
out the parent, HN, query-saving as a method for CHN to call upon entrance.

Joist internal redirects drop query strings.  Whenever a user is forced to
login before they can access a page, they are allowed access only via a
login managed by an joist internal redirect.  The argument of what to
proxy is carried in the query string.  When dropped by the joist internal
redirect, Noodle was throwing NPEs not knowing what to proxy.

Also commited the leonardr fix that stops CHN catching redirect exceptions.
Joist does redirects w/ exceptions.  CHN was catching these redirects as
real 'exceptions' rather than letting them propagate up the stack for
later processing by joist.

Reviewed by: Reviewed and tested by JRobbins
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.13.6.1 2001/04/24 22:26:48 stack Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.13.6.1 2001/04/24 22:26:48 stack Exp $";
a252 24
    /**
     * Check we're logged in and save off a noodleData object w/ query string, 
     * etc., if we are not.
     *
     * We do this because we're probably about to cross an internal redirect 
     * that goes by the Login page.  This internal redirect will drop our 
     * needed query string 'page' argument.  This method saves the current 
     * noodleData to context.  Make it protected so it can be called by 
     * CHN.  It does stuff before calling processRequest that can cause the 
     * internal redirect to happen prematurely.
     *
     * @@param context WebMacro context
     */
    protected void loggedInCheck( WebContext context )
    {
        SessionManager sMgr = getSessionManager( context );
        if( !sMgr.isLoggedIn( ).booleanValue( )
            && ( contextGet( context, DATA_KEY ) == null ) )
        {
            contextPut( context, DATA_KEY, makeNoodleData( context ) );
            if( DEBUG )
                Log.debug( "NOT LOGGED IN: contextPut " + DATA_KEY );
        }
    }   
d324 12
a335 2
        
        loggedInCheck( context );
d363 1
a363 1
            ProxyModule.proxyRequest( noodleData, "/" );
d451 1
a451 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.13.6.1 2001/04/24 22:26:48 stack Exp $";
@


1.13.6.3
log
@HelmNoodle parses first 4k of file it picks from disk looking for HTML
HEAD tags.  If it finds them, it puts the string that is between HEAD
tags into a context variable 'head'.

Currently HN cuts a character too much from the end of this string.
This commit fixes that.  That it cuts a character too much is a
items inside HEAD are abutted against the closing HEAD tag.

Reviewed by: edk
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.13.6.2 2001/05/01 05:45:17 stack Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.13.6.2 2001/05/01 05:45:17 stack Exp $";
d465 1
a465 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.13.6.2 2001/05/01 05:45:17 stack Exp $";
d535 1
a535 1
                                                          headEnd );
@


1.13.6.4
log
@Provide support for stripping HTML up to and include <body ...> and
including and after </body>.  This feature is not turned on by default.
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.13.6.3 2001/06/10 05:59:11 stack Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.13.6.3 2001/06/10 05:59:11 stack Exp $";
a160 13
    /**
     * Key to use -- from the general zone.properties file -- which allows
     * stripping of html before/after <body ...> and </body> tags.
     */
    protected final static String STRIP_AROUND_BODY =
        "HelmNoodleStripAroundBody";

    /**
     * Key to use -- on a per-servlet basis -- to determine whether the
     * stripping feature (as above, if enabled) should be used for this servlet.
     */
    protected final static String SUBCLASS_STRIP_AROUND_BODY =
        "HelmNoodleSubclassStripAroundBody";
a355 28

            // has this instance requested stripping around the body?
            String stripFlag =
                myProperties.getProperty(STRIP_AROUND_BODY);
            boolean stripAroundBody = false;
            if (stripFlag != null)
            {
                stripAroundBody = new Boolean(stripFlag).booleanValue();
                stripFlag = null;
            }

            // subclasses of HelmNoodle can say whether nor not stripping
            // up to the <body ...> tag or after the </body> tag is allowed.
            // there's no need to check if the instance doesn't want it.
            if (stripAroundBody)
            {
                stripFlag =
                    myProperties.getProperty(SUBCLASS_STRIP_AROUND_BODY);
                if (stripFlag != null)
                {
                    stripAroundBody = new Boolean(stripFlag).booleanValue();
                }
                else
                {
                    stripAroundBody = false;
                }
            }

a397 1
                    stripAroundBody = false;
a410 7

                    if (stripAroundBody)
                    {
                        responseString = stripAroundBody(context, topOfHtmlFile,
                                noodleData, consider, responseString);
                        context.put (ProxyModule.PAGE_VAR, responseString);
                    }
d465 1
a465 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.13.6.3 2001/06/10 05:59:11 stack Exp $";
a543 55


    /**
     * Used internally to return a new response string with leading and
     * trailing stuff stripped off, if appropriate.
     *
     * TODO : clean this up.  this implementation is deeply tied to the
     * caller's implementation.  It has been separated largely for visual
     * simplicity in the function above; all the logic needs to be factored
     * out.
     *
     * @@param context WebContext for this request
     * @@param topOfHtmlFile String the top consider characters of the file
     * (previously extracted for other reasons)
     * @@param noodleData HelmNoodleData the noodle data for this request
     * @@param consider int the number of characters to consider (the lesser
     * of some constant and the length of the response)
     * @@param responseString String the current response string (without
     * anything stripped)
     */
    private String stripAroundBody(WebContext context, String topOfHtmlFile,
            HelmNoodleData noodleData, int consider, String responseString)
    {
        byte data[] = noodleData.getResponseData();
        // see if we can find the <BODY ...> tag:
        int offset_start = topOfHtmlFile.indexOf("<body");
        if (offset_start >= 0)
        {
            offset_start = topOfHtmlFile.indexOf(">",
                    offset_start + "<body".length());
        }

        // see if we can find the </BODY> tag:
        String bottomOfHtmlFile = new String(
                data, data.length - consider, consider).toLowerCase();
        int offset_end =
            bottomOfHtmlFile.lastIndexOf("</body>");
        if (offset_end >= 0 && data.length > consider)
        {
            offset_end = data.length - consider + offset_end;
        }

        // TODO : perhaps we should look harder if we find one but not
        // the other?
        if (offset_start >= 0 && offset_end >= 0)
        {
            // we want the substring starting with offset_start+1, going to
            // just before offset_end 
            responseString = new String(
                    data, offset_start + 1, offset_end - offset_start - 1);
        }
        return responseString; // may be null -- that's ok
    }


@


1.13.6.5
log
@Corrected two variable names for consistancy with conventions used
throughout the rest of the Helm source code.
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.13.6.4 2001/06/21 03:14:44 edk Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.13.6.4 2001/06/21 03:14:44 edk Exp $";
d514 1
a514 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.13.6.4 2001/06/21 03:14:44 edk Exp $";
d618 2
a619 2
        int offsetStart = topOfHtmlFile.indexOf("<body");
        if (offsetStart >= 0)
d621 2
a622 2
            offsetStart = topOfHtmlFile.indexOf(">",
                    offsetStart + "<body".length());
d628 1
a628 1
        int offsetEnd =
d630 1
a630 1
        if (offsetEnd >= 0 && data.length > consider)
d632 1
a632 1
            offsetEnd = data.length - consider + offsetEnd;
d637 1
a637 1
        if (offsetStart >= 0 && offsetEnd >= 0)
d639 2
a640 2
            // we want the substring starting with offsetStart+1, going to
            // just before offsetEnd 
d642 1
a642 1
                    data, offsetStart + 1, offsetEnd - offsetStart - 1);
@


1.13.6.6
log
@Remove ContentEncoding and TransferEncoding modules.  These modules do
automatic expansion of gzipped and compressed files.  Meant that any such
file passed via noodle was automatically expanded upon download.  Probably
not what we want.

This behavior is especially problematic when the file being expanded is
large. Our JVM has upper limit of 32megs on a block allocation.  A gzipped
file of 7megs expanded in memory was causing OOME exceptions because the
expanded file was greater than our 32 meg limit.  As is, noodle has upper
bound of whatever the largest block the JVM will allocate.

Reviewed by: edk
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.13.6.5 2001/06/21 19:52:48 dlr Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.13.6.5 2001/06/21 19:52:48 dlr Exp $";
d233 3
a235 4
        // Remove the automatic handling of redirects module as well
        // as the automatic expansion of gzipped and compressed files.
        //
        try
a238 4
            HTTPConnection.removeDefaultModule( Class.forName( 
                "HTTPClient.ContentEncodingModule" ) );
            HTTPConnection.removeDefaultModule( Class.forName( 
                "HTTPClient.TransferEncodingModule" ) );
a246 2
        
        
d514 1
a514 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.13.6.5 2001/06/21 19:52:48 dlr Exp $";
@


1.12
log
@Corrected soft reference munged in last commit and added comment about
runtime dependancies.
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.11 2001/02/22 08:31:28 dlr Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.11 2001/02/22 08:31:28 dlr Exp $";
d123 1
a123 1
    private static final boolean DEBUG = true;
d418 1
a418 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.11 2001/02/22 08:31:28 dlr Exp $";
@


1.11
log
@Now we're proxying!
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.10 2001/02/21 01:08:24 dlr Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.10 2001/02/21 01:08:24 dlr Exp $";
d169 3
d205 1
a205 1
                Class.forName("NoodleCookieModule"),1);
d418 1
a418 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.10 2001/02/21 01:08:24 dlr Exp $";
@


1.10
log
@This is the highest revision from the HELM_PEER_PORT_BRANCH.  The previous import caused too many conflicts to resolve manually
@
text
@d66 1
a66 2
import org.joist.publish.ProxyModule;
import org.joist.publish.NoodleResources;
d73 1
a73 1
 * This class is org.joist.publish.Noodle only it subclasses
d77 2
a78 2
 * class reuses most of org.joist.publish.Noodle. This is possible because most 
 * of org.joist.publish.Noodle is static.
d93 1
a93 1
 * actual HTTPClient connection is done down in org.joist.publish.ProxyModule.
d100 2
a101 2
 * @@see org.joist.publish.Noodle
 * @@version $Id: HelmNoodle.java,v 1.8.2.2 2001/02/20 04:10:39 dlr Exp $
d110 1
a110 1
        = "$Id: HelmNoodle.java,v 1.8.2.2 2001/02/20 04:10:39 dlr Exp $";
d114 1
a114 1
     * org.joist.publish.Noodle. This properties file has the NoodleResources 
d159 1
a159 1
     * This code is duplication of org.joist.publish.Noodle
d167 1
a167 1
     * [GoF95] that creates its own instance of org.joist.publish.Noodle (You 
d202 1
a202 1
                Class.forName("org.joist.publish.NoodleCookieModule"),1);
d230 2
a231 2
            port = rb.getString(org.joist.publish.Noodle.DEFAULT_PORT);
            hostname = rb.getString(org.joist.publish.Noodle.DEFAULT_HOST);
d249 1
a249 1
     * org.joist.publish.Noodle.processRequest. It's an 'override'.
d251 1
a251 1
     * org.joist.publish.NoodleData class. HelmNoodleData has a webmacro 
d304 1
a304 1
            if( method.equalsIgnoreCase( org.joist.publish.ProxyModule.POST ) )
d307 1
a307 1
                    org.joist.publish.Noodle.postToByteArray(
d354 1
a354 2
            // TODO: FIXME: Can't find ProxyModule
//             ProxyModule.proxyRequest( noodleData );
d376 1
a376 1
                context.put( org.joist.publish.ProxyModule.PAGE_VAR, responseString);
d395 2
a396 2
           context.put( org.joist.publish.ProxyModule.PAGE_VAR,
                org.joist.publish.Noodle.stackTrace( e ) );
d415 1
a415 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.8.2.2 2001/02/20 04:10:39 dlr Exp $";
@


1.9
log
@Turn off the automatic handling of redirects by HTTPClient.  Rather, let the redirect go back through noodle to the browser.  The HTTPClient redirect wasn't working w/ the NYC fellas' php calendar program; It was corrupting cookies at the least.
@
text
@d2 1
a2 1
 * Copyright (c) 2000 Collab.Net.  All rights reserved.
d17 1
a17 1
 * software developed by Collab.Net <http://www.Collab.Net/>."
d32 1
a32 1
 * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
a48 5

// Java Servlet Classes
import javax.servlet.*;
import javax.servlet.http.*;
// Java Core Classes
d52 2
d57 2
a58 1
import org.webmacro.util.*;
d65 1
a65 1
import org.tigris.helm.security.HelmServlet;
d68 2
a71 2
import org.joist.session.*;

d73 1
a73 3

/** "Subclass" of org.joist.publish.Noodle.
 *
d102 1
a102 1
 * @@version $Id: HelmNoodle.java,v 1.8 2001/01/26 18:29:07 edk Exp $
d107 2
a108 1
    /** Class version string
d111 1
a111 2
        = "$Id: HelmNoodle.java,v 1.8 2001/01/26 18:29:07 edk Exp $";

d113 2
a114 1
    /** Properties file to override the properties file in
d121 2
a122 7

    /** Logging object
     */
    private static Log log = new Log( "HlmNdl", "HelmNoodle" );


    /** debug flag.
d124 1
a124 2
    public static final boolean debug = true;

d126 2
a127 1
    /** Key to put and get uri string into context
d132 2
a133 2

    /** maximum number of cached parsed head tags
d137 2
a138 1
    /** how long head cache should last, 3 minutes
d142 2
a143 1
    /** cache of parsed head tags
d147 2
a148 1
    /** time that cache should be flushed
d152 3
a154 1
    /** The most bytes that we scan when looking for <head> content */
d246 2
a247 1
    /** Process the request.
d285 2
a286 4
        // log.setLevel( log.DEBUG );
        
        if( debug )
            log.info( "Begin" );
d325 2
a326 2
            if( debug )
                log.info( "NOT LOGGED IN: contextPut " + DATA_KEY );
d334 1
a334 1
            if (debug) log.info("old TEMPLATE: " + templateName );
d350 2
a351 2
                if( debug )
                    log.info( "contextGet " + DATA_KEY );
d354 3
a356 1
            ProxyModule.proxyRequest( noodleData ); // Do the proxying.
d363 1
a363 1
                if (debug) log.info("content type: " + ct );
d365 1
a365 1
                if (debug) log.info("altTemplate: " + altTemplate );
d370 1
a370 1
                    if (debug) log.info("old TEMPLATE: " + templateName );
d374 1
a374 1
                    if (debug) log.info("new TEMPLATE: " + templateName );
d403 2
a404 2
            if( debug )
                log.info( "End" );
d411 2
a412 1
    /** Noodle Servlet
d417 1
a417 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.8 2001/01/26 18:29:07 edk Exp $";
d421 2
a422 2
    /** Build up a request uri.
     * Build up the URI using passed in request object.
d472 1
a472 1
                if (debug) log.info("flushed cachedHeadTags");
d481 1
a481 1
            if (debug) log.info("headStart = " + headStart);
d484 1
a484 1
            if (debug) log.info("headEnd = " + headEnd);
d490 1
a490 1
        if (debug) log.info("headString = " + headString + "|");
a494 1

d531 1
a531 1
                if( debug ) log.info( "Redirect to: " + location + 
@


1.8
log
@readding all the files as part of the process of returning to the way
things were as of tuesday
@
text
@d80 4
a83 5
 * org.joist.security.UnsecureServlet.  We cannot do
 * a true subclass -- HelmServlet is a subclass of
 * UnsecureServlet -- but this classes reuses
 * most of org.joist.publish.Noodle. This is possible
 * because most of org.joist.publish.Noodle is static.
d85 8
a92 11
 * This 'override' is done to add the WebMacro context to
 * NoodleData so that it is available to Noodle filters.
 * The subclassing of HelmServlet is so we can implement
 * permission checking on the user request.  When we
 * call our parent's processRequest method, it will look 
 * in our property file for the 'permission' property.  The
 * parent, HelmServlet, checks that the current request
 * has permission for this request.  It either lets us 
 * through or throws an ACLException if we're not allowed.
 * Note this scheme allows the checking of one permission
 * only.  This makes things simple. 
d94 9
d106 1
a106 1
 * @@version $Id: HelmNoodle.java,v 1.6 2001/01/16 06:51:58 jrobbins Exp $
d114 1
a114 1
        = "$Id: HelmNoodle.java,v 1.6 2001/01/16 06:51:58 jrobbins Exp $";
d118 2
a119 3
     * org.joist.publish.Noodle. This properties file has
     * the NoodleResources of the original plus
     * the filters added that depend on this class.
d160 3
a162 1
    /** Servlet start.
d165 9
a173 5
     * other than DEFAULT_PORT and DEFAULT_HOST references.
     * I have to duplicate the code because i cannot subclass
     * or make an Adapter [GoF95] that creates its own
     * instance of org.joist.publish.Noodle (You cannot
     * instantiate servlets -- only the servlet runner can).
d198 2
a199 1
        // remove the default HTTPClient.CookieModule.class
d201 1
d211 1
d214 15
d290 1
a290 1
            log.info( "BEGIN" );
d357 2
a358 1
            ProxyModule.proxyRequest( noodleData );
d360 11
a370 9
           HTTPResponse httpResponse = noodleData.getHTTPResponse();
           String ct = httpResponse.getHeader("Content-Type");
           if (debug) log.info("content type: " + ct );
           String altTemplate = (String) context.getForm("template");
           if (debug) log.info("altTemplate: " + altTemplate );

           if (!ct.startsWith("text/html") || altTemplate != null)
               {
                   if (altTemplate == null) 
d372 2
a373 2
                   if (debug) log.info("old TEMPLATE: " + templateName );
                   templateName =
d376 8
a383 11
                   if (debug) log.info("new TEMPLATE: " + templateName );
                   
               }

           
           String responseString = new String( noodleData.getResponseData( ) );
           context.put( org.joist.publish.ProxyModule.PAGE_VAR, responseString);
            
           if (ct.startsWith("text/html"))
               {
                   int consider =
d386 1
a386 1
                   String topOfHtmlFile = (new
d388 3
a390 2
                   parseHead(context, topOfHtmlFile, responseString, noodleData);
               }
d406 1
a406 1
                log.info( "END" );
d418 1
a418 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.6 2001/01/16 06:51:58 jrobbins Exp $";
d498 61
a558 1

@


1.8.2.1
log
@Porting to the Turbine Peer system.  -dlr and leonardr
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.8 2001/01/26 18:29:07 edk Exp $
d109 1
a109 1
        = "$Id: HelmNoodle.java,v 1.8 2001/01/26 18:29:07 edk Exp $";
d389 1
a389 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.8 2001/01/26 18:29:07 edk Exp $";
@


1.8.2.2
log
@Ported from HEAD.
@
text
@d2 1
a2 1
 * Copyright (c) 2000-2001 CollabNet.  All rights reserved.
d17 1
a17 1
 * software developed by CollabNet <http://www.collab.net/>."
d32 1
a32 1
 * IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
d49 5
a56 2
import javax.servlet.*;
import javax.servlet.http.*;
d60 1
a60 2
import org.apache.turbine.util.Log;

d67 1
a67 1
import org.joist.session.*;
d70 3
a73 2
import org.tigris.helm.security.HelmServlet;
import org.tigris.helm.security.AccessControlList;
d76 2
a77 1
/**
d80 5
a84 4
 * org.joist.security.UnsecureServlet as joist Noodle does.  We cannot do a 
 * true subclass -- HelmServlet is a subclass of UnsecureServlet -- but this 
 * class reuses most of org.joist.publish.Noodle. This is possible because most 
 * of org.joist.publish.Noodle is static.
d86 11
a96 8
 * This 'override' is done to add the WebMacro context to NoodleData so that it 
 * is available to Noodle filters. The subclassing of HelmServlet is so we can 
 * implement permission checking on the user request.  When we call our parent's 
 * processRequest method, it will look in our property file for the 'permission' 
 * property.  The parent, HelmServlet, checks that the current request has 
 * permission for this request.  It either lets us through or throws an 
 * ACLException if we're not allowed. Note this scheme allows the checking of 
 * one permission only.  This makes things simple. 
a97 9
 * I've turned off the automatic handling of redirects by the HTTPClient
 * library.  We came across it when trying to wrap the php calendar program w/
 * Noodle. It was messing us up.  I thought about having a properties file 
 * where you could turn this feature on and off as you needed it only the 
 * actual HTTPClient connection is done down in org.joist.publish.ProxyModule.
 * In here is where you'd want to know if the feature was wanted or not.  I 
 * would have to disturb old code.  I'll wait till someone needs such a
 * feature.  See 
 * <a href="http://www.innovation.ch/java/HTTPClient/advanced_info.html#modules">Advanced HTTPClient Info.: Modules</a>.
d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.9 2001/02/17 08:37:08 stack Exp $
d106 1
a106 2
    /**
     * Class version string
d109 2
a110 1
        = "$Id: HelmNoodle.java,v 1.9 2001/02/17 08:37:08 stack Exp $";
d112 4
a115 4
    /**
     * Properties file to override the properties file in
     * org.joist.publish.Noodle. This properties file has the NoodleResources 
     * of the original plus the filters added that depend on this class.
d120 7
a126 2
    /**
     * Debug flag.
d128 1
a128 1
    private static final boolean DEBUG = true;
d130 2
a131 2
    /**
     * Key to put and get uri string into context
d136 2
a137 2
    /**
     * Maximum number of cached parsed head tags.
d141 1
a141 2
    /**
     * How long head cache should last, 3 minutes.
d145 1
a145 2
    /**
     * Cache of parsed head tags.
d149 1
a149 2
    /**
     * Time that cache should be flushed.
d153 1
a153 3
    /**
     * The most bytes that we scan when looking for <head> content.
     */
d156 1
a156 3
    /** 
     * Servlet start.
     * 
d159 5
a163 9
     * other than DEFAULT_PORT and DEFAULT_HOST references AND we've made it so
     * the redirect module is not automatically loaded (Was causing problems 
     * w/ the calendar application).  See above in the class javadoc for 
     * comment.
     * <p>
     * I have to duplicate the code because i cannot subclass or make an Adapter 
     * [GoF95] that creates its own instance of org.joist.publish.Noodle (You 
     * cannot nstantiate servlets -- only the servlet runner can).
     * 
d188 1
a188 2
        
        // Remove the default HTTPClient.CookieModule.class
a189 1
        //
a198 1
            //
a200 15
        
        // Remove the automatic handling of redirects module too.
	    //
	    try
        {
            HTTPConnection.removeDefaultModule( Class.forName( 
                "HTTPClient.RedirectionModule" ) );
        }
        catch( ClassNotFoundException classnotfoundexception )
        {
            // Convert the exception.
            //
            throw new ServletException ("HelmNoodle: " 
                + classnotfoundexception.getMessage());
        }
d221 1
a221 2
    /**
     * Process the request.
d259 4
a262 2
        if( DEBUG )
            Log.debug( "Begin" );
d301 2
a302 2
            if( DEBUG )
                Log.info( "NOT LOGGED IN: contextPut " + DATA_KEY );
d310 1
a310 1
            if (DEBUG) Log.info("old TEMPLATE: " + templateName );
d326 2
a327 2
                if( DEBUG )
                    Log.debug( "contextGet " + DATA_KEY );
d329 1
a329 4
            
            // Do the proxying.
            // TODO: FIXME: Can't find ProxyModule
//             ProxyModule.proxyRequest( noodleData );
d331 9
a339 11
            HTTPResponse httpResponse = noodleData.getHTTPResponse( );
            
            if( ! checkForRedirect( httpResponse, context ) )
            {
                String ct = httpResponse.getHeader("Content-Type");
                if (DEBUG) Log.info("content type: " + ct );
                String altTemplate = (String) context.getForm("template");
                if (DEBUG) Log.info("altTemplate: " + altTemplate );
                if (!ct.startsWith("text/html") || altTemplate != null)
                {
                    if (altTemplate == null) 
d341 2
a342 2
                    if (DEBUG) Log.debug("old TEMPLATE: " + templateName );
                    templateName =
d345 11
a355 8
                    if (DEBUG) Log.debug("new TEMPLATE: " + templateName );
                }
    
                String responseString = new String( noodleData.getResponseData( ) );
                context.put( org.joist.publish.ProxyModule.PAGE_VAR, responseString);
                if (ct.startsWith("text/html"))
                {
                    int consider =
d358 1
a358 1
                    String topOfHtmlFile = (new
d360 2
a361 3
                    parseHead(context, topOfHtmlFile, responseString, noodleData);
                }
            } // End of check for exception
d376 2
a377 2
            if( DEBUG )
                Log.debug( "End" );
d384 1
a384 2
    /**
     * CVS <code>Id</code> token.
d389 1
a389 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.9 2001/02/17 08:37:08 stack Exp $";
d393 2
a394 2
    /**
     * Build up a request URI using passed in request object.
d444 1
a444 1
                if (DEBUG) Log.debug("flushed cachedHeadTags");
d453 1
a453 1
            if (DEBUG) Log.debug("headStart = " + headStart);
d456 1
a456 1
            if (DEBUG) Log.debug("headEnd = " + headEnd);
d462 1
a462 1
        if (DEBUG) Log.debug("headString = " + headString + "|");
d467 1
d469 1
a469 61
    
    /** 
     * Pass on the redirect.
     *
     * If noodle gets a back a statuscode of 3xx, then pass the redirect back 
     * to the browser.  We do this calling
     * org.joist.security.UnsecureServlet#aredirectToURL.  This in turn calls
     * HttpServletResponse#response which only passes a 302 when our result 
     * could have been any of the 3xx codes.  Is this why the HTTPClient does
     * the redirect for you?
     *
     * @@param inHTTPResponse The HTTPClient response.  Holds the result of the
     *        noodle request.
     * @@param inContext Webmacro context. Has everything in it.
     * 
     * @@return Returns true if we're redirecting.
     * 
     * @@exception HandlerException  Generic exception used for propogation.
     */
    private boolean checkForRedirect( HTTPResponse inHTTPResponse, 
        WebContext inContext )
            throws HandlerException, RedirectException
    {
        boolean booleanRedirectResult = false;
        try
        {
            int statusCode = inHTTPResponse.getStatusCode( );
            if( statusCode >= 300 && statusCode < 400 )
            {
                booleanRedirectResult = true;
                String strStatusCode = Integer.toString( statusCode );
                String location = inHTTPResponse.getHeader( "Location" );
                if( location == null )
                    throw new HandlerException( "No location. StatusCode: " 
                        + strStatusCode );
                if( DEBUG ) Log.info( "Redirect to: " + location + 
                    ". StatusCode = " + strStatusCode );
                redirectToURL( inContext, location );
            }
        }
        
        catch( IOException ioexception )
        {
            // Could be io when we have problem w/ socket. Do a conversion to 
            // the HendlerException
            //
            throw new HandlerException( "IOException checking for redirect: "
                + ioexception.toString( ) );
        }
        
        catch( HTTPClient.ModuleException moduleException )
        {
            // An internal HTTPClient error. Do a conversion to  the 
            // HendlerException
            //
            throw new HandlerException( "ModuleException checking for redirect: "
                + moduleException.toString( ) );
        }
        
        return booleanRedirectResult;
    }
@


1.7
log
@revert this stuff to its state as of 2001/01/24 12:42:00 GMT -- with
the following exceptions, which have some different modifications so
they'll work with changes which have happened in joist:

ProjectNewsEdit.java
SkinManager.java
SkinStyleElementValueManager.java
StyleElementManager.java
StyleManager.java

it compiles.  i dunno if it works, though.
@
text
@@


1.6
log
@added cacheing of parsed <HEAD> tags
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.5 2001/01/16 04:19:28 jrobbins Exp $
d109 1
a109 1
        = "$Id: HelmNoodle.java,v 1.5 2001/01/16 04:19:28 jrobbins Exp $";
d389 1
a389 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.5 2001/01/16 04:19:28 jrobbins Exp $";
@


1.5
log
@added capability for html titles parsed from contributed content
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.4 2000/11/29 12:38:18 jrobbins Exp $
d109 1
a109 1
        = "$Id: HelmNoodle.java,v 1.4 2000/11/29 12:38:18 jrobbins Exp $";
d136 17
d360 1
a360 1
                   parseHead(context, topOfHtmlFile, responseString);
d389 1
a389 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.4 2000/11/29 12:38:18 jrobbins Exp $";
d430 2
a431 1
    protected void parseHead(WebContext context, String topOfHtmlFile, String responseString) 
d438 12
d451 12
a462 9
        int headStart = topOfHtmlFile.indexOf(HEAD_START_TAG);
        log.info("headStart = " + headStart);
        if (headStart == -1) return;
        int headEnd = topOfHtmlFile.indexOf(HEAD_END_TAG, headStart+HEAD_START_TAG_LENGTH);
        log.info("headEnd = " + headEnd);
        if (headEnd == -1) return;
        String headString =  responseString.substring(headStart+HEAD_START_TAG_LENGTH,
                                                      headEnd - 1);
        log.info("headString = " + headString + "|");
@


1.4
log
@added ContentHelmNoodle and all of its associated files, also had to add a hook for ProxyModule to transform page name and support for alternative master templates
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.3 2000/11/29 00:00:05 stack Exp $
d109 1
a109 1
        = "$Id: HelmNoodle.java,v 1.3 2000/11/29 00:00:05 stack Exp $";
d136 2
d332 13
a344 2
            context.put( org.joist.publish.ProxyModule.PAGE_VAR,
                new String( noodleData.getResponseData( ) ) );
d372 1
a372 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.3 2000/11/29 00:00:05 stack Exp $";
d411 24
@


1.4.4.1
log
@added parsing of <head> from contributed content
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.5 2001/01/16 04:19:28 jrobbins Exp $
d109 1
a109 1
        = "$Id: HelmNoodle.java,v 1.5 2001/01/16 04:19:28 jrobbins Exp $";
a135 2
    /** The most bytes that we scan when looking for <head> content */
    public final static int MAX_HEAD_BTYES_CONSIDERED = 4000;
d330 2
a331 13
           
           String responseString = new String( noodleData.getResponseData( ) );
           context.put( org.joist.publish.ProxyModule.PAGE_VAR, responseString);
            
           if (ct.startsWith("text/html"))
               {
                   int consider =
                       MAX_HEAD_BTYES_CONSIDERED < noodleData.getResponseData().length ?
                       MAX_HEAD_BTYES_CONSIDERED : noodleData.getResponseData().length;
                   String topOfHtmlFile = (new
                       String(noodleData.getResponseData(), 0, consider)).toLowerCase();
                   parseHead(context, topOfHtmlFile, responseString);
               }
d359 1
a359 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.5 2001/01/16 04:19:28 jrobbins Exp $";
a397 24
    }

    protected void parseHead(WebContext context, String topOfHtmlFile, String responseString) 
    {
        final String TITLE_START_TAG = "<title>";
        final String HEAD_START_TAG = "<head>";
        final int HEAD_START_TAG_LENGTH = HEAD_START_TAG.length();
        final String HEAD_END_TAG = "</head>";

        context.put("headTitle", Boolean.FALSE);
        int headStart = topOfHtmlFile.indexOf(HEAD_START_TAG);
        log.info("headStart = " + headStart);
        if (headStart == -1) return;
        int headEnd = topOfHtmlFile.indexOf(HEAD_END_TAG, headStart+HEAD_START_TAG_LENGTH);
        log.info("headEnd = " + headEnd);
        if (headEnd == -1) return;
        String headString =  responseString.substring(headStart+HEAD_START_TAG_LENGTH,
                                                      headEnd - 1);
        log.info("headString = " + headString + "|");
        context.put("head", headString);

        if (topOfHtmlFile.indexOf(TITLE_START_TAG) != -1) 
            context.put("headTitle", Boolean.TRUE);

@


1.4.4.2
log
@added cacheing of parsed <HEAD> tags
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.4.4.1 2001/01/16 04:23:11 jrobbins Exp $
d109 1
a109 1
        = "$Id: HelmNoodle.java,v 1.6 2001/01/16 06:51:58 jrobbins Exp $";
a138 17
    /** maximum number of cached parsed head tags
     */
    public final static int MAX_CACHED_HEAD_TAGS = 100;

    /** how long head cache should last, 3 minutes
     */
    public final static int HEAD_CACHE_TIMEOUT = 180000;

    /** cache of parsed head tags
     */
    public static Hashtable cachedHeadTags = null;

    /** time that cache should be flushed
     */
    public static long cacheExpiresTime = 0;


d343 1
a343 1
                   parseHead(context, topOfHtmlFile, responseString, noodleData);
d372 1
a372 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.6 2001/01/16 06:51:58 jrobbins Exp $";
d413 1
a413 2
    protected void parseHead(WebContext context, String topOfHtmlFile,
                             String responseString, HelmNoodleData noodleData) 
a419 12
        if (cachedHeadTags == null || 
            cachedHeadTags.size() >= MAX_CACHED_HEAD_TAGS || 
            System.currentTimeMillis() > cacheExpiresTime) 
            {
                cachedHeadTags = new Hashtable(MAX_CACHED_HEAD_TAGS);
                cacheExpiresTime = System.currentTimeMillis() + HEAD_CACHE_TIMEOUT;
                if (debug) log.info("flushed cachedHeadTags");
            }

        // check for cached content
        String headString = (String) cachedHeadTags.get(noodleData.getURL());

d421 9
a429 12
        if (headString == null) {
            int headStart = topOfHtmlFile.indexOf(HEAD_START_TAG);
            if (debug) log.info("headStart = " + headStart);
            if (headStart == -1) return;
            int headEnd = topOfHtmlFile.indexOf(HEAD_END_TAG, headStart+HEAD_START_TAG_LENGTH);
            if (debug) log.info("headEnd = " + headEnd);
            if (headEnd == -1) return;
            headString =  responseString.substring(headStart+HEAD_START_TAG_LENGTH,
                                                          headEnd - 1);
            cachedHeadTags.put(noodleData.getURL(), headString);
        }
        if (debug) log.info("headString = " + headString + "|");
@


1.3
log
@Get current context after crossing the TLogin divide.  Was using context from before TLogin request.  Also, copious notes on issues w/ the way i'm storing across TLogin redirects
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.2 2000/11/21 01:35:07 jon Exp $
d109 1
a109 1
        = "$Id: HelmNoodle.java,v 1.2 2000/11/21 01:35:07 jon Exp $";
d197 2
a198 1
        ProxyModule.setServerName(hostname);
d243 2
a244 1
            log.debug( "BEGIN" );
d257 1
a257 4
        HelmNoodleData noodleData
            = new HelmNoodleData( context.getRequest( ),
                context.getResponse( ),
                context );
d283 1
a283 1
                log.debug( "NOT LOGGED IN: contextPut " + DATA_KEY );
d286 2
a287 2
        String templateName = super.processRequest( connection, context );
        
d290 2
d308 1
a308 1
                    log.debug( "contextGet " + DATA_KEY );
d311 19
d334 4
d340 1
a340 1
            context.put( org.joist.publish.ProxyModule.PAGE_VAR,
d347 1
a347 1
                log.debug( "END" );
d359 1
a359 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.2 2000/11/21 01:35:07 jon Exp $";
d390 10
@


1.2
log
@fixed compile errors
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.1.1.1 2000/11/12 21:16:24 dlr Exp $
d109 1
a109 1
        = "$Id: HelmNoodle.java,v 1.1.1.1 2000/11/12 21:16:24 dlr Exp $";
d204 4
a207 6
     * org.joist.publish.Noodle.processRequest.
     * It's an 'override'.
     * The difference is that it makes a HelmNoodleData
     * class in place of a org.joist.publish.NoodleData class.
     * HelmNoodleData has a webmacro context where 
     * NoodleData does not.
d209 5
a213 6
     * If not logged in, we'll be redirected to the
     * login screen by this classes' parent when
     * we call processRequest.  Save off the current
     * URI so that when TLogin brings us back to here
     * we'll have all our original info (TLogin drops
     * the query string).
d215 5
a219 4
     * When we come back from TLogin, the browser
     * location string will have '/issues/TLogin' in
     * it -- not the original request.  Need to fix
     * sometime.
d221 3
a223 4
     * Note also that the call to processRequest will 
     * redirect us to an unauthorized page if we do not
     * have access permissions.  It figures whether we
     * have permissions by reading a 'permissions' property
d244 10
a253 4
        // Build up the NoodleData.
        // Do it before processRequest in case we're not 
        // logged in.  This way we can save off the request
        // while we're redirected to TLogin.
d259 1
a259 2
        String method
            = context.getRequest( ).getMethod( );
d263 1
a263 2
            if( method.equalsIgnoreCase(
               org.joist.publish.ProxyModule.POST ) )
d286 3
a288 2

        String templateName = null;
a290 2
            templateName = super.processRequest( connection, context );

d295 8
a302 9
                // If something in the context, it was put 
                // there because we weren't logged in.  Because
                // we weren't logged in, we were redirected to
                // TLogin.  TLogin then bounced us back here.
                // On entry this time, we ARE logged in so we
                // by-pass putting a new object into context 
                // overwriting the object we'd put there before
                // the TLogin redirect.  Pull the object we 
                // stashed away and use it.
d305 1
d335 1
a335 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.1.1.1 2000/11/12 21:16:24 dlr Exp $";
@


1.1
log
@Initial revision
@
text
@d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.15 2000/11/08 04:15:53 stack Exp $
d109 1
a109 1
        = "$Id: HelmNoodle.java,v 1.15 2000/11/08 04:15:53 stack Exp $";
d258 12
a269 2
        if( method.equalsIgnoreCase(
            org.joist.publish.ProxyModule.POST ) )
d271 1
a271 3
            noodleData.setPostData(
                org.joist.publish.Noodle.postToByteArray(
                context.getRequest( ) ) );
d286 1
a286 2
        String templateName
            = super.processRequest( connection, context );
d289 2
d335 1
a335 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.15 2000/11/08 04:15:53 stack Exp $";
@


1.1.1.1
log
@Merging newhelm tree into helm, as per IZ issue #557.  Previous major version of helm is tagged TIGRIS_NOV_11_2000.
@
text
@@


1.1.1.2
log
@Replacing the HEAD with the HELM_PEER_PORT_BRANCH.
@
text
@d2 1
a2 1
 * Copyright (c) 2000-2001 CollabNet.  All rights reserved.
d17 1
a17 1
 * software developed by CollabNet <http://www.collab.net/>."
d32 1
a32 1
 * IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
d49 5
a56 2
import javax.servlet.*;
import javax.servlet.http.*;
d60 1
a60 2
import org.apache.turbine.util.Log;

d67 1
a67 1
import org.joist.session.*;
d70 3
a73 2
import org.tigris.helm.security.HelmServlet;
import org.tigris.helm.security.AccessControlList;
d76 2
a77 1
/**
d80 5
a84 4
 * org.joist.security.UnsecureServlet as joist Noodle does.  We cannot do a 
 * true subclass -- HelmServlet is a subclass of UnsecureServlet -- but this 
 * class reuses most of org.joist.publish.Noodle. This is possible because most 
 * of org.joist.publish.Noodle is static.
d86 11
a96 8
 * This 'override' is done to add the WebMacro context to NoodleData so that it 
 * is available to Noodle filters. The subclassing of HelmServlet is so we can 
 * implement permission checking on the user request.  When we call our parent's 
 * processRequest method, it will look in our property file for the 'permission' 
 * property.  The parent, HelmServlet, checks that the current request has 
 * permission for this request.  It either lets us through or throws an 
 * ACLException if we're not allowed. Note this scheme allows the checking of 
 * one permission only.  This makes things simple. 
a97 9
 * I've turned off the automatic handling of redirects by the HTTPClient
 * library.  We came across it when trying to wrap the php calendar program w/
 * Noodle. It was messing us up.  I thought about having a properties file 
 * where you could turn this feature on and off as you needed it only the 
 * actual HTTPClient connection is done down in org.joist.publish.ProxyModule.
 * In here is where you'd want to know if the feature was wanted or not.  I 
 * would have to disturb old code.  I'll wait till someone needs such a
 * feature.  See 
 * <a href="http://www.innovation.ch/java/HTTPClient/advanced_info.html#modules">Advanced HTTPClient Info.: Modules</a>.
d101 1
a101 1
 * @@version $Id: HelmNoodle.java,v 1.8.2.2 2001/02/20 04:10:39 dlr Exp $
d106 1
a106 2
    /**
     * Class version string
d109 2
a110 1
        = "$Id: HelmNoodle.java,v 1.8.2.2 2001/02/20 04:10:39 dlr Exp $";
d112 4
a115 4
    /**
     * Properties file to override the properties file in
     * org.joist.publish.Noodle. This properties file has the NoodleResources 
     * of the original plus the filters added that depend on this class.
a119 4
    /**
     * Debug flag.
     */
    private static final boolean DEBUG = true;
d121 1
a121 2
    /**
     * Key to put and get uri string into context
d123 1
a123 2
    public final static String DATA_KEY
        = "helmnoodle.data";
a124 4
    /**
     * Maximum number of cached parsed head tags.
     */
    public final static int MAX_CACHED_HEAD_TAGS = 100;
d126 1
a126 2
    /**
     * How long head cache should last, 3 minutes.
d128 1
a128 1
    public final static int HEAD_CACHE_TIMEOUT = 180000;
a129 4
    /**
     * Cache of parsed head tags.
     */
    public static Hashtable cachedHeadTags = null;
d131 1
a131 2
    /**
     * Time that cache should be flushed.
d133 2
a134 1
    public static long cacheExpiresTime = 0;
a135 4
    /**
     * The most bytes that we scan when looking for <head> content.
     */
    public final static int MAX_HEAD_BTYES_CONSIDERED = 4000;
d137 1
a137 3
    /** 
     * Servlet start.
     * 
d140 5
a144 9
     * other than DEFAULT_PORT and DEFAULT_HOST references AND we've made it so
     * the redirect module is not automatically loaded (Was causing problems 
     * w/ the calendar application).  See above in the class javadoc for 
     * comment.
     * <p>
     * I have to duplicate the code because i cannot subclass or make an Adapter 
     * [GoF95] that creates its own instance of org.joist.publish.Noodle (You 
     * cannot nstantiate servlets -- only the servlet runner can).
     * 
d169 1
a169 2
        
        // Remove the default HTTPClient.CookieModule.class
a170 1
        //
a179 1
            //
a181 15
        
        // Remove the automatic handling of redirects module too.
	    //
	    try
        {
            HTTPConnection.removeDefaultModule( Class.forName( 
                "HTTPClient.RedirectionModule" ) );
        }
        catch( ClassNotFoundException classnotfoundexception )
        {
            // Convert the exception.
            //
            throw new ServletException ("HelmNoodle: " 
                + classnotfoundexception.getMessage());
        }
d197 1
a197 2
        //ProxyModule.setServerName(hostname);
        ProxyModule.setServerName("localhost");
d201 1
a201 2
    /**
     * Process the request.
d204 6
a209 4
     * org.joist.publish.Noodle.processRequest. It's an 'override'.
     * The difference is that it makes a HelmNoodleData class in place of a 
     * org.joist.publish.NoodleData class. HelmNoodleData has a webmacro 
     * context where NoodleData does not.
d211 6
a216 5
     * If not logged in, we'll be redirected to the login screen by this 
     * classes' parent whenwe call processRequest.  Save off the current
     * URI, etc. so that when TLogin brings us back to here we'll have all our 
     * original info (TLogin drops the query string).  We do this by saving 
     * a HelmNoodleData object into the context.
d218 4
a221 5
     * When we come back from TLogin, the browser location string will have 
     * '/issues/TLogin' in it -- not the original request.  Need to fix
     * sometime.  Also, there are issues w/ using a HelmNoodleData that has
     * gone across a TLogin -- it will have 'old' stuff in it... objects from
     * the request before TLogin.  Revisit.
d223 4
a226 3
     * Note also that the call to processRequest will  redirect us to an 
     * unauthorized page if we do not have access permissions.  It figures 
     * whether we have permissions by reading a 'permissions' property
d242 4
a245 3
        if( DEBUG )
            Log.debug( "Begin" );

d247 4
a250 10
        // Build up the NoodleData. Do it before processRequest in case we're 
        // not logged in.  This way we can save off the request while we're 
        // redirected to TLogin.  This technique is problematic because it is
        // preserving old data.  I should be more selective about what it is 
        // that i want to preserve.  I'm uncertain as to what should be saved
        // at time of writing (11/28/2000) mostly because i'm unsure of the 
        // TLogin redirect mechanism -- what it does and doesn't preserve
        // (query strings, POST data, etc.).  During the writing of this code
        // WebMacro was upgraded adding to the uncertainty (It broke context
        // handling across TLogin -- edk added contextGet to fix).
d252 12
a263 15
        HelmNoodleData noodleData = makeNoodleData(context);
        String method = context.getRequest( ).getMethod( );

        try
        {
            if( method.equalsIgnoreCase( org.joist.publish.ProxyModule.POST ) )
            {
                noodleData.setPostData(
                    org.joist.publish.Noodle.postToByteArray(
                    context.getRequest( ) ) );
            }
        }
        catch (Exception e)
        {
            // ignore
d274 2
a275 2
            if( DEBUG )
                Log.info( "NOT LOGGED IN: contextPut " + DATA_KEY );
a276 2
        
        String templateName = null;
d278 2
a281 2
            templateName = super.processRequest( connection, context );
            if (DEBUG) Log.info("old TEMPLATE: " + templateName );
d286 9
a294 8
                // If something in the context, it was put there because we 
                // weren't logged in.  Because we weren't logged in, we were 
                // redirected to TLogin.  TLogin then bounced us back here.
                // On entry this time, we ARE logged in so we by-pass putting 
                // a new object into context   overwriting the object we'd put 
                // there before the TLogin redirect.  Pull the object we 
                // stashed away and use it.  'Freshen' the context it has. 
                // It's context is pre-TLogin unless i freshen it.
d297 2
a298 3
                noodleData.setContext( context );
                if( DEBUG )
                    Log.debug( "contextGet " + DATA_KEY );
d300 3
a302 40
            
            // Do the proxying.
            // TODO: FIXME: Can't find ProxyModule
//             ProxyModule.proxyRequest( noodleData );

            HTTPResponse httpResponse = noodleData.getHTTPResponse( );
            
            if( ! checkForRedirect( httpResponse, context ) )
            {
                String ct = httpResponse.getHeader("Content-Type");
                if (DEBUG) Log.info("content type: " + ct );
                String altTemplate = (String) context.getForm("template");
                if (DEBUG) Log.info("altTemplate: " + altTemplate );
                if (!ct.startsWith("text/html") || altTemplate != null)
                {
                    if (altTemplate == null) 
                       altTemplate = myProperties.getProperty("binaryTemplate");
                    if (DEBUG) Log.debug("old TEMPLATE: " + templateName );
                    templateName =
                       myProperties.getProperty("SERVLET_TEMPLATE_ROOT") + "/" +
                       altTemplate;
                    if (DEBUG) Log.debug("new TEMPLATE: " + templateName );
                }
    
                String responseString = new String( noodleData.getResponseData( ) );
                context.put( org.joist.publish.ProxyModule.PAGE_VAR, responseString);
                if (ct.startsWith("text/html"))
                {
                    int consider =
                       MAX_HEAD_BTYES_CONSIDERED < noodleData.getResponseData().length ?
                       MAX_HEAD_BTYES_CONSIDERED : noodleData.getResponseData().length;
                    String topOfHtmlFile = (new
                       String(noodleData.getResponseData(), 0, consider)).toLowerCase();
                    parseHead(context, topOfHtmlFile, responseString, noodleData);
                }
            } // End of check for exception
        }

        catch (RedirectException re) {
            throw re;
d307 1
a307 1
           context.put( org.joist.publish.ProxyModule.PAGE_VAR,
d313 2
a314 2
            if( DEBUG )
                Log.debug( "End" );
d321 1
a321 2
    /**
     * CVS <code>Id</code> token.
d326 1
a326 1
        return "HelmNoodle Servlet: $Id: HelmNoodle.java,v 1.8.2.2 2001/02/20 04:10:39 dlr Exp $";
d330 2
a331 2
    /**
     * Build up a request URI using passed in request object.
a355 109
    }

    protected HelmNoodleData makeNoodleData(WebContext context) 
    {
        HelmNoodleData result 
            = new HelmNoodleData( context.getRequest( ),
                context.getResponse( ),
                context );
        return result;
    }

    protected void parseHead(WebContext context, String topOfHtmlFile,
                             String responseString, HelmNoodleData noodleData) 
    {
        final String TITLE_START_TAG = "<title>";
        final String HEAD_START_TAG = "<head>";
        final int HEAD_START_TAG_LENGTH = HEAD_START_TAG.length();
        final String HEAD_END_TAG = "</head>";

        if (cachedHeadTags == null || 
            cachedHeadTags.size() >= MAX_CACHED_HEAD_TAGS || 
            System.currentTimeMillis() > cacheExpiresTime) 
            {
                cachedHeadTags = new Hashtable(MAX_CACHED_HEAD_TAGS);
                cacheExpiresTime = System.currentTimeMillis() + HEAD_CACHE_TIMEOUT;
                if (DEBUG) Log.debug("flushed cachedHeadTags");
            }

        // check for cached content
        String headString = (String) cachedHeadTags.get(noodleData.getURL());

        context.put("headTitle", Boolean.FALSE);
        if (headString == null) {
            int headStart = topOfHtmlFile.indexOf(HEAD_START_TAG);
            if (DEBUG) Log.debug("headStart = " + headStart);
            if (headStart == -1) return;
            int headEnd = topOfHtmlFile.indexOf(HEAD_END_TAG, headStart+HEAD_START_TAG_LENGTH);
            if (DEBUG) Log.debug("headEnd = " + headEnd);
            if (headEnd == -1) return;
            headString =  responseString.substring(headStart+HEAD_START_TAG_LENGTH,
                                                          headEnd - 1);
            cachedHeadTags.put(noodleData.getURL(), headString);
        }
        if (DEBUG) Log.debug("headString = " + headString + "|");
        context.put("head", headString);

        if (topOfHtmlFile.indexOf(TITLE_START_TAG) != -1) 
            context.put("headTitle", Boolean.TRUE);
    }
    
    /** 
     * Pass on the redirect.
     *
     * If noodle gets a back a statuscode of 3xx, then pass the redirect back 
     * to the browser.  We do this calling
     * org.joist.security.UnsecureServlet#aredirectToURL.  This in turn calls
     * HttpServletResponse#response which only passes a 302 when our result 
     * could have been any of the 3xx codes.  Is this why the HTTPClient does
     * the redirect for you?
     *
     * @@param inHTTPResponse The HTTPClient response.  Holds the result of the
     *        noodle request.
     * @@param inContext Webmacro context. Has everything in it.
     * 
     * @@return Returns true if we're redirecting.
     * 
     * @@exception HandlerException  Generic exception used for propogation.
     */
    private boolean checkForRedirect( HTTPResponse inHTTPResponse, 
        WebContext inContext )
            throws HandlerException, RedirectException
    {
        boolean booleanRedirectResult = false;
        try
        {
            int statusCode = inHTTPResponse.getStatusCode( );
            if( statusCode >= 300 && statusCode < 400 )
            {
                booleanRedirectResult = true;
                String strStatusCode = Integer.toString( statusCode );
                String location = inHTTPResponse.getHeader( "Location" );
                if( location == null )
                    throw new HandlerException( "No location. StatusCode: " 
                        + strStatusCode );
                if( DEBUG ) Log.info( "Redirect to: " + location + 
                    ". StatusCode = " + strStatusCode );
                redirectToURL( inContext, location );
            }
        }
        
        catch( IOException ioexception )
        {
            // Could be io when we have problem w/ socket. Do a conversion to 
            // the HendlerException
            //
            throw new HandlerException( "IOException checking for redirect: "
                + ioexception.toString( ) );
        }
        
        catch( HTTPClient.ModuleException moduleException )
        {
            // An internal HTTPClient error. Do a conversion to  the 
            // HendlerException
            //
            throw new HandlerException( "ModuleException checking for redirect: "
                + moduleException.toString( ) );
        }
        
        return booleanRedirectResult;
@

