/*
 * Created on May 26, 2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package org.owasp.webgoat.lessons;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.ecs.Element;
import org.apache.ecs.ElementContainer;
import org.apache.ecs.html.A;
import org.apache.ecs.html.BR;
import org.apache.ecs.html.Input;
import org.apache.ecs.html.P;
import org.apache.ecs.html.TD;
import org.apache.ecs.html.TR;
import org.apache.ecs.html.Table;
import org.owasp.webgoat.session.DatabaseUtilities;
import org.owasp.webgoat.session.ECSFactory;
import org.owasp.webgoat.session.ParameterNotFoundException;
import org.owasp.webgoat.session.WebSession;

/**
 * @author asmolen
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class SoapRequest extends LessonAdapter {
	/* TEST CODE
	private final static String ACCT_NUM = "account_number";
	private static Connection connection = null;
	private String accountNumber;
	*/
	
	//static boolean completed;
	public static Connection connection = null;
    public final static String firstName = "getFirstName";
    public final static String lastName = "getLastName";
    public final static String loginCount = "getLoginCount";
    public final static String ccNumber = "getCreditCard";
    
    //int instead of boolean to keep track of method invocation count
    static int accessFirstName;
    static int accessLastName;
    static int accessCreditCard;
    static int accessLoginCount;
    
    protected Category getDefaultCategory()
	{
		return AbstractLesson.WEB_SERVICES;
	}

	protected List getHints()
	{
		List<String> hints = new ArrayList<String>();
		hints.add("Accessible operations are delimited by the &lt;operation&gt; tag contained within the &lt;portType&gt; section of the WSDL. <BR> Below is an example of a typical operation (getFirstName): <br><br>" +
				"&lt;wsdl:portType name=\"SoapRequest\"&gt; <br>" +
				"&lt;wsdl:<strong>operation name=\"getFirstName\"</strong>&gt;<br>" +
				"&lt;wsdl:input message=\"impl:getFirstNameRequest\" name=\"getFirstNameRequest\" /&gt;<br>" +
				"&lt;wsdl:output message=\"impl:getFirstNameResponse\" name=\"getFirstNameResponse\" /&gt;<br>" +
				"&lt;wsdlsoap:operation soapAction=\"\" /&gt;" +
				"&lt;/wsdl:portType&gt;<br><br>" +
				"The methods invoked are defined by the input and output message attributes. " +
				"Example: <strong>\"getFirstNameRequest\"</strong>");
		hints.add("There are several tags within a SOAP envelope. " + 
				"Each namespace is defined in the &lt;definitions&gt; section of the WSDL, and is declared using the (xmlns:namespace_name_here=\"namespace_reference_location_here\") format.<br><br>" + 
				"The following example defines a tag \"&lt;xsd:\", whose attribute structure will reference the namespace location assigned to it in the declaration:<br>" +
				"<strong>xmlns:xsd=\"http://www.w3.org/2001/XMLSchema</strong>");
		hints.add("Determine what parameters and types are required by the message definition corresponding to the operation's request method. " + 
				"This example defines a parameter (id) of type (int) in the namespace (xsd) for the method (getFirstNameRequest):<br>" +
				"&lt;wsdl:message name=\"getFirstNameRequest\"<br><br>" +
				"&lt;wsdl:<strong>part name=\"id\" type=\"xsd:int\"</strong> /&gt;<br>" +
				"&lt;/wsdl:message&gt;<br><br>" +
				"Examples of other types:<br>" + 
				"{boolean, byte, base64Binary, double, float, int, long, short, unsignedInt, unsignedLong, unsignedShort, string}.<br>");
		String soapEnv = "A SOAP request uses the following HTTP header: <br><br> " +
		        "SOAPAction: some action header, can be &quot;&quot; <br><br>" +
			    "The SOAP message body has the following format:<br>" +
				"&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt; <br>" +
				"&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" <br>" +
				"	                  xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" <br>" +
				"	                  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"&gt; <br>" +
				"&nbsp;&nbsp;&lt;SOAP-ENV:Body&gt; <br>" +
				"&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:getFirstName SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:ns1=\"http://lessons\"&gt; <br>" +
				"&nbsp;&nbsp;&nbsp;&nbsp;&lt;id xsi:type=\"xsd:int\"&gt;101&lt;/id&gt; <br>"+
				"&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ns1:getFirstName&gt; <br>" +
				"&nbsp;&nbsp;&lt;/SOAP-ENV:Body&gt; <br>" +
				"&lt;/SOAP-ENV:Envelope&gt; <br><br>" +
				"Intercept the HTTP request and try to create a SOAP request.";
		soapEnv.replaceAll("(?s) ","&nbsp;");
		hints.add(soapEnv);
		
		return hints;
	}

	private final static Integer DEFAULT_RANKING = new Integer(100);

	protected Integer getDefaultRanking()
	{
		return DEFAULT_RANKING;
	}

	public String getTitle()
	{
		return "How to Create a SOAP Request";
	}
	
	protected Element makeOperationsLine( WebSession s )
	{
		ElementContainer ec = new ElementContainer();
		
		Table t1 = new Table().setCellSpacing( 0 ).setCellPadding( 2 );
		
		if ( s.isColor() )
		{
			t1.setBorder( 1 );
		}
			
		TR tr = new TR();
		tr.addElement(new TD().addElement( "How many operations are defined in the WSDL: " ));
		tr.addElement(new TD( new Input( Input.TEXT, "count", "")));
		Element b = ECSFactory.makeButton( "Submit" ); 
		tr.addElement( new TD(b).setAlign("LEFT") );
		t1.addElement(tr);
		
		ec.addElement(t1);

		return ec;
	}
	
	protected Element makeTypeLine( WebSession s )
	{
		ElementContainer ec = new ElementContainer();
		
		Table t1 = new Table().setCellSpacing( 0 ).setCellPadding( 2 );
		
		if ( s.isColor() )
		{
			t1.setBorder( 1 );
		}
			
		TR tr = new TR();
		tr.addElement(new TD().addElement( "Now, what is the type of the (id) parameter in the \"getFirstNameRequest\" method: " ));
		tr.addElement(new TD( new Input( Input.TEXT, "type", "")));
		Element b = ECSFactory.makeButton( "Submit" ); 
		tr.addElement( new TD(b).setAlign("LEFT") );
		t1.addElement(tr);
		
		ec.addElement(t1);

		return ec;
	}

	protected Element createContent( WebSession s )
	{
		return super.createStagedContent(s);
	}
	
	protected Element doStage1( WebSession s ) throws Exception
	{
		return viewWsdl( s );
	}
	
	protected Element doStage2( WebSession s ) throws Exception
	{
		return determineType( s);
	}
	
	protected Element doStage3( WebSession s ) throws Exception
	{
		return createSoapEnvelope( s);
	}
	
	protected Element viewWsdl(WebSession s)
	{
		ElementContainer ec = new ElementContainer();
		
		//DEVNOTE: Test for stage completion.
	   	try
		{
			int operationCount = 0;
			operationCount = s.getParser().getIntParameter( "count" );
			
			if (operationCount == 4)
			{
				getLessonTracker(s).setStage(2);
				s.setMessage("Stage 1 completed.");
				
				// Redirect user to Stage2 content.
				ec.addElement(doStage2(s));
			}
			else
			{
				s.setMessage( "Sorry, that is an incorrect count. Try Again." );
			}
		}
	   	catch (NumberFormatException nfe)
		{
			//DEVNOTE: Eat the exception.
	   		//ec.addElement( new P().addElement( nfe.getMessage() ) );
	   		s.setMessage("Sorry, that answer is invalid. Try again.");
		}
		catch (ParameterNotFoundException pnfe)
		{
			//DEVNOTE: Eat the exception.
			// ec.addElement( new P().addElement( pnfe.getMessage() ) );
		}
		catch ( Exception e )
		{
			s.setMessage( "Error generating " + this.getClass().getName() );
			e.printStackTrace();
		}
		
		//DEVNOTE: Conditionally display Stage1 content depending on whether stage is completed or not
		if (getLessonTracker(s).getStage() == 1)
		//if ( null == (getLessonTracker(s).getLessonProperties().getProperty(WebSession.STAGE)) ||
		//	(getLessonTracker(s).getLessonProperties().getProperty(WebSession.STAGE)).equals("1") )
		{
			ec.addElement( makeOperationsLine(s) );
			
			A a = new A("services/SoapRequest?WSDL","WebGoat WSDL");
			ec.addElement(new P().addElement("View the following WSDL and count available operations:"));
			ec.addElement(new BR());
			ec.addElement(a);
		}
		
		//getLessonTracker( s ).setCompleted( SoapRequest.completed );
		
		return (ec);
	}
	
	protected Element determineType(WebSession s)
	{
		ElementContainer ec = new ElementContainer();
		
		//DEVNOTE: Test for stage completion.
	   	try
		{
			String paramType = "";
			paramType = s.getParser().getStringParameter( "type" );
			
			//if (paramType.equalsIgnoreCase("int"))
			if (paramType.equals("int"))
			{
				getLessonTracker(s).setStage(3);
				s.setMessage("Stage 2 completed. ");
				//s.setMessage("Now, you'll craft a SOAP envelope for invoking a web service directly.");
				
				// Redirect user to Stage2 content.
				ec.addElement(doStage3(s));
			}
			else
			{
				s.setMessage( "Sorry, that is an incorrect type. Try Again." );
			}
		}
		catch (ParameterNotFoundException pnfe)
		{
			//DEVNOTE: Eat the exception.
			// ec.addElement( new P().addElement( pnfe.getMessage() ) );
		}
		catch ( Exception e )
		{
			s.setMessage( "Error generating " + this.getClass().getName() );
			e.printStackTrace();
		}
		
		//DEVNOTE: Conditionally display Stage2 content depending on whether stage is completed or not
		if (getLessonTracker(s).getStage() == 2)
		//if ( null == (getLessonTracker(s).getLessonProperties().getProperty(WebSession.STAGE)) ||
		//	(getLessonTracker(s).getLessonProperties().getProperty(WebSession.STAGE)).equals("2") )
		{
			ec.addElement( makeTypeLine(s) );
			
			A a = new A("services/SoapRequest?WSDL","WebGoat WSDL");
			ec.addElement(new P().addElement("View the following WSDL and count available operations:"));
			ec.addElement(new BR());
			ec.addElement(a);
		}
		
		//getLessonTracker( s ).setCompleted( SoapRequest.completed );
		
		return (ec);
	}
	
	protected Element createSoapEnvelope (WebSession s)
	{
		ElementContainer ec = new ElementContainer();
        
        // Determine how many methods have been accessed. User needs to check at least two methods
        // before completing the lesson.
        if ((accessFirstName + accessLastName + accessCreditCard + accessLoginCount) >= 2)
        {
        	/** Reset function access counters **/
        	accessFirstName = accessLastName = accessCreditCard = accessLoginCount = 0;
        	//SoapRequest.completed = true;
        	makeSuccess(s);
        }
        else
        {

        	// display Stage2 content
            ec.addElement(new P().addElement( "Intercept the request and invoke any method by sending a valid SOAP request for a valid account. <br>" ));
    		Element b = ECSFactory.makeButton( "Press to generate an HTTP request" );        
            ec.addElement( b );
            
        	// conditionally display invoked methods
            if ((accessFirstName + accessLastName + accessCreditCard + accessLoginCount) > 0)
            {
            	ec.addElement("<br><br>Methods Invoked:<br>");
            	ec.addElement("<ul>");
            	if ( accessFirstName > 0 )
            	{
            		ec.addElement("<li>getFirstName</li>");
            	}
            	if ( accessLastName > 0 )
            	{
            		ec.addElement("<li>getLastName</li>");
            	}
            	if ( accessCreditCard > 0 )
            	{
            		ec.addElement("<li>getCreditCard</li>");
            	}
            	if ( accessLoginCount > 0 )
            	{
            		ec.addElement("<li>getLoginCount</li>");
            	}
            	ec.addElement("</ul>");
            }
        }
        
        //getLessonTracker( s ).setCompleted( SoapRequest.completed );
		return (ec);
	}
	
	public String getResults(int id, String field) {
		try
		{				
			Connection connection = DatabaseUtilities.makeConnection();
			if (connection == null) {
				return null;
			}
			PreparedStatement ps = connection.prepareStatement("SELECT * FROM user_data WHERE userid = ?");
			ps.setInt(1, id);
			try
			{
				ResultSet results = ps.executeQuery();
				if ( ( results != null ) && ( results.next() == true ) )
				{						
					return results.getString(field);
				}				
			}
			catch ( SQLException sqle )
			{
			}
		}
		catch ( Exception e )
		{
		}
		return null;
	}
	
	public String getCreditCard(int id) {
		String result = getResults(id, "cc_number");
		//SoapRequest.completed = true;
		
		if (result != null)
		{
			//DEVNOTE: Always set method access counter to (1) no matter how many times it is accessed.
			// This is intended to be used to determine how many methods have been accessed, not how often.
			accessCreditCard = 1;
			return result;
		}
		return null;
	}
	
	public String getFirstName(int id) {
		String result = getResults(id, "first_name");
		if (result != null)
		{
			//DEVNOTE: Always set method access counter to (1) no matter how many times it is accessed.
			// This is intended to be used to determine how many methods have been accessed, not how often.
			accessFirstName = 1;
			return result;
		}
		return null;
	}
	
    public String getLastName(int id) {
        String result = getResults(id, "last_name");
        if (result != null)
        {
			//DEVNOTE: Always set method access counter to (1) no matter how many times it is accessed.
			// This is intended to be used to determine how many methods have been accessed, not how often.
        	accessLastName = 1;
            return result;
        }
        return null;
    }
    
    public String getLoginCount(int id) {
        String result = getResults(id, "login_count");        
        if (result != null)
        {
			//DEVNOTE: Always set method access counter to (1) no matter how many times it is accessed.
			// This is intended to be used to determine how many methods have been accessed, not how often.
        	accessLoginCount = 1;
            return result;
        }
        return null;
    }    
}
