package org.owasp.webgoat.session;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import javax.servlet.ServletContext;

import org.owasp.webgoat.HammerHead;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.Category;

/**
 *  Copyright (c) 2002 Free Software Foundation developed under the custody of the Open Web
 *  Application Security Project (http://www.owasp.org) This software package org.owasp.webgoat.is published by OWASP
 *  under the GPL. You should read and accept the LICENSE before you use, modify and/or redistribute
 *  this software.
 *
 * @author     Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
 * @created    October 28, 2003
 */
public class Course
{
	private List lessons = new ArrayList();
	private final static String PROPERTIES_FILENAME = HammerHead.propertiesPath;
	private WebgoatProperties properties = null;

	public Course()
	{
		try
		{
			properties = new WebgoatProperties(PROPERTIES_FILENAME);
		}
		catch (IOException e)
		{
			System.out.println("Error loading WebGoat properties");
			e.printStackTrace();
		}
	}
	
	
	/**
	 *  Description of the Method
	 *
	 * @param  fileName  Description of the Parameter
	 * @param  path      Description of the Parameter
	 * @param  ext       Description of the Parameter
	 * @return           Description of the Return Value
	 */
	private String clean( String fileName, String path, String ext )
	{
		fileName = fileName.trim();

		// check if file is a directory
		if ( fileName.endsWith( "/" ) )
		{
			return fileName;
		}

		// check if file is a class or java file
		if ( !fileName.endsWith( ext ) )
		{
			return null;
		}

		// if the file is in /WEB-INF/classes strip the dir info off
		int index = fileName.indexOf( "/WEB-INF/classes/" );
		if ( index != -1 )
		{
			fileName = fileName.substring( index + "/WEB-INF/classes/".length(), fileName.length() - ext.length() );
			fileName = fileName.replace( '/', '.' );
			fileName = fileName.replace( '\\', '.' );
		}
		else
		{
			// Strip off the leading path info
			fileName = fileName.substring( path.length(), fileName.length() - ext.length() );
		}

		return fileName;
	}


    /**
     *  Description of the Method
     * @param  lesson      Description of the Parameter
     * @param  context     Description of the Parameter
     * @param  path        Description of the Parameter
     * @param  courseName  Description of the Parameter
     * @param extension TODO
     */
    private void findSourceResource( AbstractLesson lesson, ServletContext context, String path, String className, String extension )
    {
        //System.out.println("findSourceResource() looking for source files in: " + path);
        //System.out.println("findSourceResource() looking for source files for class: " + className);
        Set files = context.getResourcePaths( path );
        Iterator fileIter = files.iterator();
        String resource = null;
        
        while ( fileIter.hasNext() )
        {
             resource = (String) fileIter.next();
             //System.out.println("findSourceResource() inspecting resource: " + resource);
             String lessonName = clean( resource, path, extension );
             //System.out.println("findSourceResource() cleaned resource name: " + lessonName);
             //if ( className != null )
             //{
             //	System.out.println("Resource to check: " + resource);
             //	System.out.println("Lesson name: " + lessonName);
            //}

             // Not a match
            if ( lessonName == null )
            {
                continue;
            }
            // A subdirectory
            else if ( ( lessonName.length() != 1 ) && lessonName.endsWith( "/" ) )
            {
                findSourceResource( lesson, context, lessonName, className, extension );
            }
            // A source file
            else
            {
                // Course name will be the fully qualified name: 
                // like lesson.admin.lessonName
                if ( className.endsWith( lessonName ) )
                {
                    int length = 0;
                    int index = className.indexOf("admin.");
                    if ( index == -1 )
                    {
                        index = className.indexOf("lessons.");
                        length = "lessons.".length();
                    } 
                    else 
                    {
                        length = "admin.".length();
                    }
                    className = className.substring(index + length);
                    //System.out.println("Resource to check: " + resource);
                    //System.out.println("Lesson name: " + lessonName);
 
                    //store the web path of the source file in the lesson
                    lesson.setSourceFileName(resource);
 
                }
            }
        }
    }


    /**
     *  Description of the Method
     * @param  lesson      Description of the Parameter
     * @param  context     Description of the Parameter
     * @param  path        Description of the Parameter
     * @param  courseName  Description of the Parameter
     * @param extension TODO
     */
    private void findLessonPlanResource( AbstractLesson lesson, ServletContext context, String path, String courseName, String extension )
    {
        Set files = context.getResourcePaths( path );
        Iterator fileIter = files.iterator();
        String resource = null;
        
        while ( fileIter.hasNext() )
        {
             resource = (String) fileIter.next();
             String className = clean( resource, path, extension );
             //if ( className != null )
             //{
             //    System.out.println("ClassName: " + className);
             //    System.out.println("ResourceToCheck: " + resourceToCheck);
             //}

            if ( className == null )
            {
                continue;
            }
            else if ( ( className.length() != 1 ) && className.endsWith( "/" ) )
            {
                findLessonPlanResource( lesson, context, className, courseName, extension );
            }
            else
            {
                // Course name will be the fully qualified name: 
                // like lesson.admin.lessonName
                if ( courseName.endsWith( className ) )
                {
                    int length = 0;
                    int index = courseName.indexOf("admin.");
                    if ( index == -1 )
                    {
                        index = courseName.indexOf("lessons.");
                        length = "lessons.".length();
                    } 
                    else 
                    {
                        length = "admin.".length();
                    }
                    courseName = courseName.substring(index + length);
                    //System.out.println("ClassName: " + className);
                    //System.out.println("ResourceToCheck: " + resource);
 
                    //store the web path of the source file in the lesson
                    lesson.setLessonPlanFileName(resource);
 
                }
            }
        }
    }


	/**
	 *  Gets the categories attribute of the Course object
	 *
	 * @return    The categories value
	 */
	public List getCategories()
	{
		List categories = new ArrayList();
		Iterator iter = lessons.iterator();

		while ( iter.hasNext() )
		{
			AbstractLesson lesson = (AbstractLesson) iter.next();

			if ( !categories.contains( lesson.getCategory() ) )
			{
				categories.add( lesson.getCategory() );
			}
		}

		Collections.sort( categories );

		return categories;
	}


	/**
	 *  Gets the firstLesson attribute of the Course object
	 *
	 * @return    The firstLesson value
	 */
	public AbstractLesson getFirstLesson()
	{	
		List roles = new ArrayList();
		roles.add( AbstractLesson.USER_ROLE );
		// Category 0 is the admin function.  We want the first real category 
		// to be returned. This is noramally the General category and the Http Basics lesson
		return ((AbstractLesson)getLessons( (Category)getCategories().get(1), roles).get(0));
	}


	/**
	 *  Gets the lesson attribute of the Course object
	 *
	 * @param  lessonId  Description of the Parameter
	 * @param  role      Description of the Parameter
	 * @return           The lesson value
	 */
	public AbstractLesson getLesson( WebSession s, int lessonId, List roles )
	{
		if (s.isHackedAdmin())
		{
			roles.add(AbstractLesson.HACKED_ADMIN_ROLE);
		}
		//System.out.println("getLesson() with roles: " + roles);
		Iterator iter = lessons.iterator();

		while ( iter.hasNext() )
		{
			AbstractLesson lesson = (AbstractLesson) iter.next();

			//System.out.println("getLesson() at role: " + lesson.getRole());
			if ( lesson.getScreenId() == lessonId && roles.contains(lesson.getRole()) )
			{
				return lesson;
			}
		}

		return null;
	}

	public AbstractLesson getLesson( WebSession s, int lessonId, String role )
	{
		List roles = new Vector();
		roles.add(role);
		return getLesson(s, lessonId, roles);
	}

	public List getLessons( WebSession s, String role )
	{
		List roles = new Vector();
		roles.add(role);
		return getLessons(s, roles);
	}

	/**
	 *  Gets the lessons attribute of the Course object
	 *
	 * @param  role  Description of the Parameter
	 * @return       The lessons value
	 */
	public List getLessons( WebSession s, List roles )
	{
		if (s.isHackedAdmin())
		{
			roles.add(AbstractLesson.HACKED_ADMIN_ROLE);
		}
		List lessonList = new ArrayList();
		Iterator categoryIter = getCategories().iterator();

		while ( categoryIter.hasNext() )
		{
			lessonList.addAll( getLessons( s, (Category) categoryIter.next(), roles ) );
		}
		return lessonList;
	}


	/**
	 *  Gets the lessons attribute of the Course object
	 *
	 * @param  category  Description of the Parameter
	 * @param  role      Description of the Parameter
	 * @return           The lessons value
	 */
	private List getLessons( Category category, List roles )
	{
		List lessonList = new ArrayList();

		Iterator iter = lessons.iterator();
		while ( iter.hasNext() )
		{
			AbstractLesson lesson = (AbstractLesson) iter.next();

			if ( lesson.getCategory().equals( category ) && roles.contains(lesson.getRole()) )
			{
				lessonList.add( lesson );
			}
		}

		Collections.sort( lessonList );
//		System.out.println(java.util.Arrays.asList(lessonList));
		return lessonList;
	}

	public List getLessons( WebSession s, Category category, String role )
	{
		List roles = new Vector();
		roles.add(role);
		return getLessons(s, category, roles);
	}

	public List getLessons(WebSession s, Category category, List roles)
	{
		if (s.isHackedAdmin())
		{
			roles.add(AbstractLesson.HACKED_ADMIN_ROLE);
		}
		return getLessons(category, roles);
	}

	/**
	 *  Description of the Method
	 *
	 * @param  path     Description of the Parameter
	 * @param  context  Description of the Parameter
	 */
	public void loadCourses( boolean enterprise, ServletContext context, String path )
	{
		Set files = context.getResourcePaths( path );
		Iterator fileIter = files.iterator();

		while ( fileIter.hasNext() )
		{
			String file = (String) fileIter.next();
			String className = clean( file, path, ".class" );

			//if ( className != null )
			//{
			//	System.out.println( "Checking file: " + file );
			//	System.out.println( "        class: " + className );
			//}
			if ( className == null  )
            {
                continue;
            }
            else if ( ( className.length() != 1 ) && className.endsWith( "/" ) ) 
			{
				loadCourses( enterprise, context, className );
			}
			else
			{
				Class lessonClass = null;
				try
				{
					lessonClass = Class.forName( className );
					Object possibleLesson = lessonClass.newInstance();

					if ( possibleLesson instanceof AbstractLesson )
					{
						AbstractLesson lesson = (AbstractLesson) possibleLesson;
						
						// Determine if the screen is to be loaded.  Look 
						// to see if the session parameter has been initialized.
						// Look to see if the screen is an enterprise edition screen.
						if ( !enterprise ) 
						{
							if ( lesson.isEnterprise() )
							{
								continue;
							}
						}
						
						// Do not load instructor screens.  Currently, they must be manually deployed.
						if (lesson.getClass().getName().indexOf("instructor") > -1)
							continue;
                         
                         // There are two methods instead of one because the developer was not
                        // smart enough to figure out the recursive return value
                         findSourceResource( lesson, context, "/", className, ".java" );
                         findLessonPlanResource( lesson, context, "/", className, ".html" );
						
						// Override lesson attributes based on properties.
						lesson.update(properties);
								
						if(lesson.getHidden() == false)
							lessons.add( lesson );
						//System.out.println( "Found lesson: " + lesson );
					}
				}
				catch ( Exception e )
				{
					//System.out.println("Could not load lesson: " + className);
					//e.printStackTrace();
				}
			}
		}
	}
}

