///
// HTTPReader.java
//
// Portions of this file are subject to the SNIA Public License Version 1.0
// (the "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
//    http://www.snia.org/resources/openSource.html
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is HTTPReader.java
// 
// The Initial Developer of the Original Code is David Simons.
//
// Contributor(s): Jef Poskanzer (see separate license below) 
//
// Portions (c)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
//
// Portions (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html
//
// Portions (c)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
//
// This file is based on Serve.java from Acme labs - Please see further license
// restrictions below.
//
//
// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. 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.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS 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 THE AUTHOR OR 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.
//
// Visit the ACME Labs Java page for up-to-date versions of this and other
// fine Java utilities: http://www.acme.com/java/
//
//
//
// This source uses the Solaris WBEM APIs version 2.2. 
// Synchronization will be maintained with the Solaris WBEM APIS,
// which are being standardized through the Java Community Process.
//
//
//
// $Id: HTTPReader.java,v 1.1 2003/07/17 18:32:55 kumpf Exp $
//
// $Log: HTTPReader.java,v $
// Revision 1.1  2003/07/17 18:32:55  kumpf
// HP-BAPU: PEP-64 CIM Client and CIM Listener Java Interface
//
// Revision 1.2  2002/05/23 11:36:37  prakash
// Implementing the read() function to overcome incompatibilities between different Xerces versions.
// Fix provided by Steve Jerman.
//
// Revision 1.1.1.1  2001/10/31 18:17:17  karl
// import of SNIA 0_7
//
// Revision 1.12  2001/07/22 13:22:20  davidsi
// - Added access control. Thanks to Viktor and Quan Wang.
//
// Revision 1.11  2001/06/23 11:31:37  davidsi
// - Support for SSL.
//
//
//



//
// $Id: HTTPReader.java,v 1.1 2003/07/17 18:32:55 kumpf Exp $
//
// $Log: HTTPReader.java,v $
// Revision 1.1  2003/07/17 18:32:55  kumpf
// HP-BAPU: PEP-64 CIM Client and CIM Listener Java Interface
//
// Revision 1.2  2002/05/23 11:36:37  prakash
// Implementing the read() function to overcome incompatibilities between different Xerces versions.
// Fix provided by Steve Jerman.
//
// Revision 1.1.1.1  2001/10/31 18:17:17  karl
// import of SNIA 0_7
//
// Revision 1.12  2001/07/22 13:22:20  davidsi
// - Added access control. Thanks to Viktor and Quan Wang.
//
// Revision 1.11  2001/06/23 11:31:37  davidsi
// - Support for SSL.
//
// Revision 1.3  2001/03/01 15:49:21  mihajlov
// Read all available data from socket when receiving authentication request.
//
// Revision 1.2  2001/02/28 11:38:26  mihajlov
// Added authorization support.
//
// Revision 1.10  2000/11/06 19:05:22  davidsi
// - Added flag for determining whether authorization is req'd or not.
// - Added support for being able to operate on the client side (again!).
//
// Revision 1.9  2000/08/16 05:59:59  shawn
// merge P_TOMCAT31 branch
//
// Revision 1.8.6.1  2000/08/10 00:03:36  shawn
// head merge
//
// Revision 1.8  2000/07/10 18:39:44  davidsi
// - Added support for authentication.
//
// Revision 1.7  2000/05/12 11:23:21  davidsi
// - Fixed bug whereby zero bytes being fetched from the connection could
//   result in a buffer underrun.
// - Added javadoc.
//
// Revision 1.6  2000/05/11 10:11:23  davidsi
// - Fixed tabs.
//
// Revision 1.5  2000/05/06 17:18:51  davidsi
// - Now reads direct from the network socket rather than using multiple
//   layers of indirection.
//
// Revision 1.4  2000/04/09 14:30:34  davidsi
// - Added support for HTTP 1.1 and 1.0.
// - Added support for responses that rely on terminating the connection
//   rather than providing a Content-Length.
// - Made thread safe
//
// Revision 1.3  2000/02/13 15:10:04  davidsi
// - Added text to cover re-use of Solaris WBEM APIs.
//
// Revision 1.2  2000/02/06 18:00:54  davidsi
// - BUG FIX. Temporary directory now created if it does not already
//   exist.
//
// Revision 1.1.1.1  2000/02/06 16:41:02  davidsi
// Initial version.
//
//
package org.snia.wbemcmd.xml;

import java.io.*;
import java.util.*;
import java.net.*;
import java.util.Vector;
import org.snia.wbemcmd.utils.Log;
import org.snia.wbemcfg.GlobalConfig;


public class HTTPReader extends InputStream 
{
    private static HTTPCounter globalCount=new HTTPCounter(GlobalConfig.maxThreadCounter*10);
    
    private static int HTTP_VER_BAD=0;
    private static int HTTP_VER_10=1;
    private static int HTTP_VER_11=2;
    
    private XMLParse xml;
    private InputStream in;
    private String reqProtocol = null;
    private String reqURL = null;
    private boolean forceClose=false;        // TRUE if client wants connection to close
    private Vector headers=new Vector();                  

    private int bufSize=0;
    private int bufPos=0;
    private boolean http11Request=true;
    private int maxBufSize=8192;
    private byte[] inputBuf=new byte[maxBufSize];
    static int copyBufferSize=8192;
    byte[] copyBuffer=new byte[copyBufferSize];     // for processing lines
    int httpVersion=HTTP_VER_BAD;
    String contentLength=null;    
    int length=0;
    String realm="";
    Authorizer authorizer=null;
    
    /**
     * Constructor for HTTPReader. Intended for use on the serverside
     *
     * 
     * @param socket 
     *   The socket from which the HTTP stream is to be read.
     * @param hostName
     *   The name of the host acting as the server
     * @param
     *   The port where the server is running
     * @throws IOException
     */
    public HTTPReader(Socket socket, String hostName, int portNo ) throws IOException
    {
        in =socket.getInputStream();
        xml=new XMLParse(this);
        realm = hostName + ":" + portNo;
    }
    
    /**
     * Constructor for HTTPReader - intended for use on the client
     * side of the connection
     *
     * @param socket 
     *   The socket from which the HTTP stream is to be read.
     * @throws IOException
     */
    public HTTPReader(Socket socket ) throws IOException
    {
        in =socket.getInputStream();
        xml=new XMLParse(this);
    }


    /**
     * Determines whether the connection was forced to close
     *
     * @return true if the connection has been broken
     */
    public boolean isForceClose() 
    {
        return(forceClose);
    }
    

    /**
     * Returns the headers from the HTTP request
     *
     * @return Vector of strings containing the HTTP headers
     */
    public Vector getHeaders() 
    {
        return(headers);
    }

// BAPU: added for 2.0
    /**
     * Returns the headers from the HTTP request
     *
     * @return Vector of strings containing the HTTP headers
     */
    public String getRequestURL() 
    {
        return(reqURL);
    }
    
    /**
     * Determines whether the connection is HTTP 1.1 
     *
     * @return true if the connection is HTTP 1.1
     */
    public boolean isHTTP11Request() 
    {
        return(http11Request);
    }

        
    /**
     * Converts a HTTP byte stream into the equivalent XML
     *
     * @param isAResponse true if the HTTP stream is expected to
     * be a response
     * @param returnedHeaders
     *    A vector containing additional headers that should
     *    be written to the output stream
     * @return an XMLNode representing the HTTP byte stream
     * @throws IOException
     * @throws HTTPException
     */
    public XMLNode parseRequest(Vector returnedHeaders,boolean isAResponse) 
                        throws IOException, HTTPException 
    {
        String line;
        boolean isbody=false;    
        boolean isMPOST=false;
        forceClose=false;
        bufSize=0;
        bufPos=0;
        length=0;
        contentLength=null; 
        boolean authenticated=false;
        String requestedRealm="";
    
        if (GlobalConfig.DEBUG_XML)
            Log.startTransaction();
    
        headers.removeAllElements();
        //
        // RFC 2616 states leading empty lines must be ignored
        //
        do 
        {
            line = readLine(in );
            if (GlobalConfig.DEBUG_XML)
                Log.printlnTransaction("IN:"+line);
        } while ( line==null || line.length()==0 ) ;


        String[] tokens = Utils.splitStr( line );
        reqURL = tokens[1];
        switch ( tokens.length )    
        {
            case 3:
                reqProtocol = tokens[2];
                if (!isAResponse) 
                {
                    if (tokens[2].equalsIgnoreCase("HTTP/1.0")) 
                    {
                        httpVersion=HTTP_VER_10;
                        // forceClose=true;
                        http11Request=false;
                        if (GlobalConfig.DEBUG_XML)
                            Log.printlnTransaction("IN: handling a HTTP 1.0 request");
                    } 
                    else if (tokens[2].equalsIgnoreCase("HTTP/1.1")) 
                    {
                        httpVersion=HTTP_VER_11;
                        if (GlobalConfig.DEBUG_XML)
                            Log.printlnTransaction("IN: handling a HTTP 1.1 request");
                        http11Request=true;
                    } 
                }

                while ( true ) 
                {
                    line = readLine(in  );
                    if ( line==null ||line.length()==0 )
                        break;
        
                    if (GlobalConfig.DEBUG_XML)
                        Log.printlnTransaction("IN:"+line);
                    int colonBlank = line.indexOf( ": " );
                    if ( colonBlank != -1 )         
                    {
                        String name = line.substring( 0, colonBlank );
                        String kludge=name.toLowerCase();
                        int index=kludge.indexOf("content-length");
        
                        if (index!=-1) 
                        {
                            contentLength = line.substring( colonBlank + 2 );
                            continue;
                        }

                        index=name.indexOf("Authorization");
                        //
                        // Look for an authorization
                        //
                        if (index!=-1) 
                        {
                            String authorized=line.substring( colonBlank + 2 );
                            //
			    // 03/25/02 - Added by Bapu 
			    // We should only do this if requireAuthorization is
			    // set true
			    //
			    if ( GlobalConfig.requireAuthorization )
                                authenticated=performBasicAuthentication(authorized);
                            continue;
                        }
                        index=name.indexOf("Connection");
                        //
                        // From RFC2616. A server MAY assume a client intends
                        // to maintain a persistent connection unless there is
                        // a "close" connection-token.
                        //
                        if (index!=-1) 
                        {
                            if (line.substring( colonBlank + 2 ).indexOf("close")!=-1) 
                            {
                                //
                                // Force close of connection
                                //
                                forceClose=true;
                            }
                            continue;
                        }

                        headers.addElement(line);
                    }
                }
                break;
            default:
                //
                // Attn: 06/17/2002 ( Bapu )
                // could be "400 Bad Method", 
                // "501 Not Implemented" or "510 Not Extended"
                //
                //Log.println("HTTPException: Further attention needed for this request");

                //forceClose=true;
                //throw new HTTPException(HTTPException.HTTP_ERR_UNKNOWN,
                //                        HTTPWriter.SC_BAD_REQUEST);
        }

        if (isAResponse) 
        {
            //
            // added support for "400 Bad Method" 
            //
            if (tokens[1].equals("400")) 
            {
                Log.println("HTTPException: This is a BAD request(400) ");
                throw new HTTPException(HTTPException.HTTP_ERR_BAD_REQUEST,
                HTTPWriter.SC_BAD_REQUEST);
            }

            //
            // support for "501 Not Implemented" or "510 Not Extended" 
            //
            if ( tokens[1].equals("501") || tokens[1].equals("510") )
            {
                Log.println("HTTPException: Method Not Implemented(501) or Not Extended(510)");
                throw new HTTPException(HTTPException.HTTP_ERR_NOT_IMPLEMENTED,
                    HTTPWriter.SC_NOT_IMPLEMENTED);
            }

            //
            // Server sent a challenge for the username
            //
            if (tokens[1].equals("401")) 
            {
                //
                // handle 401 Unauthrized with or without body
                //
                while (available() > 0) 
                { 
                    // empty the socket
                    byte trash[] = new byte[available()];
                    read(trash,0,trash.length);
                }
                throw new HTTPException(HTTPException.HTTP_ERR_AUTHENTICATE,
                    HTTPWriter.SC_UNAUTHORIZED);
            }

            if (tokens[0].equalsIgnoreCase("HTTP/1.0")) 
            {
                httpVersion=HTTP_VER_10;
                //forceClose=true;
                http11Request=false;
                if (GlobalConfig.DEBUG_XML)
                    Log.printlnTransaction("IN: handling a HTTP 1.0 response");
            } 
            else if (tokens[0].equalsIgnoreCase("HTTP/1.1")) 
            {
                httpVersion=HTTP_VER_11;
                if (GlobalConfig.DEBUG_XML)
                    Log.printlnTransaction("IN: handling a HTTP 1.1 response");
                http11Request=true;
            } 
        } 
        else 
        {
            if (tokens[0].equalsIgnoreCase("OPTIONS") && contentLength==null) 
            {
                if (!authenticated && optionsRequireLogin()) 
                {
                    returnedHeaders.addElement("WWW-Authenticate: Basic \"" + realm + "\"");
                    throw new HTTPException(HTTPException.HTTP_ERR_AUTHENTICATE,
                        HTTPWriter.SC_UNAUTHORIZED);
                }
                throw new HTTPException(HTTPException.HTTP_ERR_OPTIONSREQUESTED,
                    HTTPWriter.SC_ACCEPTED);
            }

            if (tokens[0].equalsIgnoreCase("M-POST"))
            {
                isMPOST=true;
            }
            else if (!tokens[0].equalsIgnoreCase("POST")) 
            {
                //
                // handle 401 Unauthrized with or without body
                //
                Log.println("Forced close in strange connection");
                forceClose=true;
                throw new HTTPException(HTTPException.HTTP_ERR_BAD_REQUEST,
                    HTTPWriter.SC_BAD_REQUEST); 
            }
        }
        if (httpVersion==HTTP_VER_BAD) 
        {
            throw new HTTPException(HTTPException.HTTP_ERR_UNKNOWN,
                HTTPWriter.SC_BAD_REQUEST);
        }
        if (!isAResponse && !authenticated && GlobalConfig.requireAuthorization ) 
        {
            returnedHeaders.addElement("WWW-Authenticate: Basic \"" + realm + "\"");
            throw new HTTPException(HTTPException.HTTP_ERR_AUTHENTICATE,
                HTTPWriter.SC_UNAUTHORIZED);
        }        
        //
        // If there was no content length, then the message is determined
        // by the connection dropping (see 4.4 in RFC 2616) but for 1.1
        // we can require a content length
        //
        if (httpVersion==HTTP_VER_11 || httpVersion==HTTP_VER_10 ) 
        {
            if (contentLength==null) 
            {
                //
                // The March 2000 interop event showed that this check is
                // far too aggressive, many implementations expect to close
                // the connection rather than send a content length, so for
                // the moment, we need to accept such transmissions...
                //
                forceClose=true;
                // throw new HTTPException(HTTPException.HTTP_ERR_NEEDCONTENTLEN,
                //            HTTPWriter.SC_BAD_REQUEST);
            } 
            else 
            {
                length=new Integer(contentLength).intValue();
                if (length==0) 
                {
                    return(null);
                }
            }
        }
        if (GlobalConfig.DEBUG_XML)
            Log.printlnTransaction("IN: (fetching body)");
    
        if (tokens[0].equalsIgnoreCase("OPTIONS")) 
        {
            throw new HTTPException(HTTPException.HTTP_ERR_OPTIONSREQUESTED,
                HTTPWriter.SC_ACCEPTED);
        }
        if (httpVersion!=HTTP_VER_11 && httpVersion!=HTTP_VER_10 ) 
            throw new HTTPException(HTTPException.HTTP_ERR_UNKNOWN,
                HTTPWriter.SC_BAD_REQUEST);
        XMLNode node=xml.parse();
        return(node);
    }



    /**
     * Overrides read() in InputStream to process the input stream
     * that may have already been cached
     */
    public int read( byte b[], int off, int len ) throws IOException 
    {
        int i,j;

        //
        // Get body
        //
        if (GlobalConfig.DEBUG_XML) 
        {
            Log.printlnTransaction("IN: message body reads: bufPos="+bufPos+
                    " bufSize="+bufSize+" Want="+len);
        }

        if (contentLength==null) 
        {
            //
            // Process requests with unknown length
            //
            //
            // Copy remaining contents of the buffer
            //
            if (bufSize>0) 
            {
                int newlen=len;
                if (newlen>bufSize)
                    newlen=bufSize;
                for(j=0,i=bufPos;j<newlen;j++,i++)
                    b[j]=inputBuf[i];
                bufSize -= j;
                bufPos=i;
                if (GlobalConfig.DEBUG_XML) 
                {
                    Log.printlnTransaction("IN (BODY CACHE with no content len):"+
                        new String(b,0,j));
                }
                return(j);
            }
            int cnt=in.read(b,off,len);
            if (GlobalConfig.DEBUG_XML) 
            {
                Log.printlnTransaction("IN with no content len read length="+cnt);
                if (cnt>=0)
                    Log.printlnTransaction("IN with no content len :"+new String(b,off,cnt));
            }
            return(cnt);
        } 
        else 
        {
            //
            // Process requests with known length
            //
            if (bufSize>0) 
            {
                //
                // Copy remaining contents of the buffer
                //
                int newlen=len;
                if (newlen>bufSize)
                    newlen=bufSize;
                for(j=0,i=bufPos;j<newlen;j++,i++)
                    b[j]=inputBuf[i];
                bufSize -= j;
                bufPos=i;
                length -= j;
                if (GlobalConfig.DEBUG_XML) 
                {
                    Log.printlnTransaction("IN (BODY CACHE):"+
                            new String(b,0,j));
                }
                return(j);
            }
            if (GlobalConfig.DEBUG_XML)
                Log.printlnTransaction("remaining characters to read :"+length);
            if (len>length)
                len=length;
            if (len==0)
                return(-1);
            int cnt=in.read(b,off,len);
            if (cnt<0)
                return(cnt);
            length -= cnt;
            if (GlobalConfig.DEBUG_XML)
                Log.printlnTransaction("IN :"+new String(b,off,cnt));
            return(cnt);
        }
    }

    
    /**
     * Overrides available() in InputStream
     */
    public int available() throws IOException 
    {
        // must account for buffered input
        return(in.available()+bufSize);
    }


    /**
     * Overrides close() in InputStream
     */
    public void close() throws IOException {
    }



    /**
     * Overrides mark() in InputStream
     */
    public synchronized void mark( int readlimit ) {
    }


    /**
     * Overrides markSupported() in InputStream
     */
    public boolean markSupported() 
    {
        return(false);
    }

    /**
     * Overrides read() in InputStream
     */
    public int read() throws IOException 
    {
        if (GlobalConfig.DEBUG_XML) 
        {
            Log.printlnTransaction("IN: read single byte, bufsize = "+bufSize);
        }

        if (bufSize>0) 
        {
            bufSize--;
            length--;
            return inputBuf[bufPos++];
        } 
        else 
        {
            readIntoBuffer(in);
            if (bufSize==0)
                return -1;
            else 
            {
                bufSize--;
                length--;
                return inputBuf[bufPos++];
            }
        }
    }
                                                          
    /**
     * Overrides reset() in InputStream
     */
    public synchronized void reset() throws IOException {
    }

    
    /**
     * Overrides skip() in InputStream
     */
    public long skip( long n ) throws IOException {
    // NOT IMPLEMENTED
    return(n);
    }

    /**
     * returns the authorizer and exposes it to caller
     */
    public Authorizer getAuthorizer() { return authorizer; };

    /**
     *
     * Loads a private buffer
     *
     * @param in the input stream from which the line of text is
     * to be taken.
     * @throws IOException io error occurred.
     */
    private void readIntoBuffer(InputStream in) throws IOException 
    {
        bufPos=0;
        bufSize=in.read(inputBuf, 0, maxBufSize);
        if (bufSize<0) 
        {
            if (GlobalConfig.DEBUG_XML)
            {
                 Log.printlnTransaction("HTTPReader:socket read failed, no data");
            }
            bufSize=0;
            throw new IOException("IO terminated in HTTP headers");
        }
    }

    /**
     *
     * Returns a line of text from an input stream with buffering
     *
     * @param in the input stream from which the line of text is
     * to be taken.
     * @return a string that contains the next line or null if 
     * no line.
     * @throws IOException io error occurred.
     */
    private String readLine(InputStream in) throws IOException
    {
        int i=0;
    
        for(;;) 
        {
            if (bufSize==0) 
            {
                readIntoBuffer(in);
                if (bufSize==0)
                    break;
            }
            char buf=(char)(inputBuf[bufPos++]);
            bufSize--;
        
            if (buf==10)
                break;
            if (buf==13)
                continue;
            copyBuffer[i++]=(byte)buf;
        }
        if (i==0) 
        {
            return(null);
        }
        return(new String(copyBuffer,0,i));
    }

   /**
    * Loads an authorization module
    */
    private void loadAuthorizer() 
    {
        if (authorizer==null) 
        {
            String authenticationModule=
                org.snia.wbemcfg.GlobalConfig.basicAuthModule;
            try 
            {
                Class cp=Class.forName(authenticationModule);
                if (cp==null) 
                {
                    Log.println("Failed to load class:"+authenticationModule);
                }
                authorizer=(Authorizer)cp.newInstance();
                authorizer.init();
            } catch(InstantiationException ie) {
                Log.println("failed to instantiate "+ie);
            }  catch(IllegalAccessException ie) {
                Log.println("failed to instantiate (IllegalAccessException) "+ie);
            } catch (ClassNotFoundException cfe) {
                Log.println("failed to load provider for "+authenticationModule); 
            }
        }
    }
    
    
   /**
    * Decodes a Basic cookie into a user name and password then
    * validates whether the user is valid
    * 
    * @param info
    *     An encoded username/password cookie
    * @return TRUE if user is authenticated
    */
    private boolean performBasicAuthentication(String info) 
    {
        String decoded;
    
        int index=info.indexOf("Basic");
    
        if (index==-1) 
        {
            return(false);
        }
        String cookie = info.substring( index + 6 );
        
        // Decode the credentials:
        try 
        {
            Base64Decoder b  = new Base64Decoder (cookie) ;
            decoded          = b.processString() ;
        } catch (Base64FormatException e) {
            Log.println("Invalid BASE64 encoding of credentials in "+cookie);
            return(false);
        }
         
        // Get user and password:
        int icolon = decoded.indexOf (':') ;
        if ( (icolon > 0) && (icolon+1 < decoded.length()) ) 
        {
            String name=decoded.substring (0, icolon) ;
            String password=decoded.substring (icolon+1) ;

            if (authorizer==null)
                loadAuthorizer();
        
            if (authorizer==null) 
            {
                Log.println("Authorization module not found");
                return(false);
            }
            return(authorizer.authenticate(name,password));
        } 
        else 
        {
            Log.println("Invalid credentials syntax in " + decoded);
            return(false);
        }
    }
    
   /**
    * Determines whether options info requires authentication
    * 
    * @return TRUE if options require authentication
    */
    private boolean optionsRequireLogin() 
    {
        if (authorizer==null)
            loadAuthorizer();
        
        if (authorizer==null) 
        {
            Log.println("Authorization module not found");
            return(false);
        }
        return(    authorizer.optionsRequireLogin());
    }
    
}

