/* $Id: MockSessionWebService.java 1756230 2016-08-12 22:20:00Z kwright $ */

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.manifoldcf.crawler.connectors.webcrawler.tests;

import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.thread.QueuedThreadPool;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import java.io.*;
import java.util.*;

/** Mock web service that requires session authentication */
public class MockSessionWebService
{
  Server server;
  SessionWebServlet servlet;
    
  public MockSessionWebService(int numContentDocs, String userName, String password)
  {
    server = new Server(new QueuedThreadPool(100));
    ServerConnector connector = new ServerConnector(server);
    connector.setPort(8191);
    server.addConnector(connector);
    servlet = new SessionWebServlet(numContentDocs,userName,password);
    ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
    context.setInitParameter("org.eclipse.jetty.servlet.SessionIdPathParameterName","none");
    context.setContextPath("/web");
    server.setHandler(context);
    context.addServlet(new ServletHolder(servlet), "/*");
  }
    
  public void start() throws Exception
  {
    server.start();
  }
    
  public void stop() throws Exception
  {
    server.stop();
  }

  
  public static class SessionWebServlet extends HttpServlet
  {
    protected final int contentPageCount;
    protected final String loginUser;
    protected final String loginPassword;
    
    public SessionWebServlet(int contentPageCount, String loginUser, String loginPassword)
    {
      this.contentPageCount = contentPageCount;
      this.loginUser = loginUser;
      this.loginPassword = loginPassword;
    }
    
    @Override
    public void service(HttpServletRequest req, HttpServletResponse res)
      throws IOException
    {
      try {
        // This mock web service resolves the following urls:
        // /index.html (an index of all N content pages)
        // /protectedcontent.html?id=N  (N content pages)
        // /loginpage.html (the login page, rendered either as a form,
        //    or as a redirection back to the content page, or as a redirection to the index page)
        
        // Get path part of request URL
        String pathPart = req.getPathInfo();
        if (pathPart == null)
        {
          // 404
          generateMissingPageResponse(res);
        }
        else
        {
          if (pathPart.equals("/loginpage.html"))
          {
            // Login page logic
            String id = req.getParameter("id");
            Integer idNumber;
            if (id == null)
              idNumber = null;
            else
              idNumber = new Integer(id);
            
            HttpSession session = req.getSession();
            Object loginInfo = session.getAttribute("logininfo");
            if (loginInfo != null && (loginInfo instanceof Boolean) && (((Boolean)loginInfo).booleanValue()))
            {
              // Already logged in: redirect back to content or index
              generateLoginRedirectPage(res,idNumber);
            }
            else
            {
              String userName = req.getParameter("user");
              String password = req.getParameter("password");
            
              if (userName == null || password == null || !loginUser.equals(userName) || !loginPassword.equals(password))
              {
                generateLoginFormPage(res,idNumber);
              }
              else
              {
                // Login succeeded, so set the session properly
                session.setAttribute("logininfo",new Boolean(true));
                generateLoginRedirectPage(res,idNumber);
              }
            }
          }
          else if (pathPart.equals("/protectedcontent.html"))
          {
            // Content page logic
            String id = req.getParameter("id");
            if (id == null)
            {
              generateBadArgumentResponse(res);
            }
            else
            {
              Integer idNumber = new Integer(id);
              if (idNumber.intValue() >= contentPageCount)
              {
                generateMissingPageResponse(res);
              }
              else
              {
                HttpSession session = req.getSession();
                Object loginInfo = session.getAttribute("logininfo");
                if (loginInfo != null && (loginInfo instanceof Boolean) && (((Boolean)loginInfo).booleanValue()))
                {
                  // Return content
                  generateContentDisplayPage(res,idNumber.intValue());
                }
                else
                {
                  // Redirect to login page
                  generateContentRedirectPage(res,idNumber.intValue());
                }
              }
            }
          }
          else if (pathPart.equals("/index.html"))
          {
            // Index logic
            HttpSession session = req.getSession();
            Object loginInfo = session.getAttribute("logininfo");
            if (loginInfo != null && (loginInfo instanceof Boolean) && (((Boolean)loginInfo).booleanValue()))
            {
              // Return content
              generateIndexDisplayPage(res,contentPageCount);
            }
            else
            {
              generateIndexRedirectPage(res);
            }
          }
          else
          {
            generateMissingPageResponse(res);
          }
        }
      }
      catch (IOException e)
      {
        e.printStackTrace();
        throw e;
      }
    }

    protected static void generateMissingPageResponse(HttpServletResponse res)
      throws IOException
    {
      res.sendError(HttpServletResponse.SC_NOT_FOUND);
    }
    
    protected static void generateBadArgumentResponse(HttpServletResponse res)
      throws IOException
    {
      res.sendError(HttpServletResponse.SC_BAD_REQUEST);
    }
    
    protected static void generateLoginRedirectPage(HttpServletResponse res, Integer returnID)
      throws IOException
    {
      String redirectTarget;
      if (returnID == null)
        redirectTarget = "/web/index.html";
      else
        redirectTarget = "/web/protectedcontent.html?id="+returnID;
      res.sendRedirect(redirectTarget);
    }
    
    protected static void generateLoginFormPage(HttpServletResponse res, Integer returnID)
      throws IOException
    {
      String actionURI = "/web/loginpage.html";
      if (returnID != null)
        actionURI += "?id="+returnID;
      res.setStatus(HttpServletResponse.SC_OK);
      res.setContentType("text/html; charset=utf-8");

      res.getWriter().printf(Locale.ROOT, "<html>\n");
      res.getWriter().printf(Locale.ROOT, "  <body>\n");
      res.getWriter().printf(Locale.ROOT, "    <form name=\"login\" action=\""+actionURI+"\">\n");
      res.getWriter().printf(Locale.ROOT, "      User name: <input type=\"text\" name=\"user\" value=\"\" size=\"20\"/>\n");
      res.getWriter().printf(Locale.ROOT, "      Password: <input type=\"password\" name=\"password\" value=\"\" size=\"20\"/>\n");
      res.getWriter().printf(Locale.ROOT, "      <input type=\"submit\"/>\n");
      res.getWriter().printf(Locale.ROOT, "    </form>\n");
      res.getWriter().printf(Locale.ROOT, "  </body>\n");
      res.getWriter().printf(Locale.ROOT, "</html>\n");
      
      res.getWriter().flush();

    }
    
    protected static void generateContentRedirectPage(HttpServletResponse res, int itemNumber)
      throws IOException
    {
      String redirectTarget = "/web/loginpage.html?id="+itemNumber;
      res.sendRedirect(redirectTarget);
    }

    protected static void generateContentDisplayPage(HttpServletResponse res, int itemNumber)
      throws IOException
    {
      res.setStatus(HttpServletResponse.SC_OK);
      res.setContentType("text/html; charset=utf-8");

      res.getWriter().printf(Locale.ROOT, "<html>\n");
      res.getWriter().printf(Locale.ROOT, "  <body>This is the document content for item "+itemNumber+"</body>");
      res.getWriter().printf(Locale.ROOT, "</html>\n");
      
      res.getWriter().flush();
    }
    
    protected static void generateIndexRedirectPage(HttpServletResponse res)
      throws IOException
    {
      String redirectTarget = "/web/loginpage.html";
      res.sendRedirect(redirectTarget);
    }
    
    protected static void generateIndexDisplayPage(HttpServletResponse res, int countItems)
      throws IOException
    {
      res.setStatus(HttpServletResponse.SC_OK);
      res.setContentType("text/html; charset=utf-8");

      res.getWriter().printf(Locale.ROOT, "<html>\n");
      res.getWriter().printf(Locale.ROOT, "  <body>\n");

      for (int i = 0; i < countItems; i++)
      {
        generateContentLink(res,i);
      }
      
      res.getWriter().printf(Locale.ROOT, "  </body>\n");
      res.getWriter().printf(Locale.ROOT, "</html>\n");
      res.getWriter().flush();

    }
    
    protected static void generateContentLink(HttpServletResponse res, int itemNumber)
      throws IOException
    {
      res.getWriter().printf(Locale.ROOT, "    <a href=\"/web/protectedcontent.html?id="+itemNumber+"\">Item "+itemNumber+"</a>\n");
    }

  }
}
