///////////////////////////////////////////////////////////////////////////////
//
//     Copyright (c) 2009-2015 Denim Group, Ltd.
//
//     The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/
//
//     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 ThreadFix.
//
//     The Initial Developer of the Original Code is Denim Group, Ltd.
//     Portions created by Denim Group, Ltd. are Copyright (C)
//     Denim Group, Ltd. All Rights Reserved.
//
//     Contributor(s):
//              Denim Group, Ltd.
//              Secure Decisions, a division of Applied Visions, Inc
//
////////////////////////////////////////////////////////////////////////
package burp.custombutton;

import burp.*;
import burp.dialog.ConfigurationDialogs;
import burp.dialog.UrlDialog;
import burp.extention.BurpPropertiesManager;
import burp.extention.RequestMakerThread;
import com.denimgroup.threadfix.data.entities.RouteParameter;
import com.denimgroup.threadfix.data.enums.ParameterDataType;
import com.denimgroup.threadfix.data.interfaces.Endpoint;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: stran
 * Date: 12/30/13
 * Time: 2:28 PM
 * To change this template use File | Settings | File Templates.
 */
public abstract class EndpointsButton extends JButton
{
    public static int mode;
    public static final String GENERIC_INT_SEGMENT = "\\{id\\}";
    public EndpointsButton(final Component view, final IBurpExtenderCallbacks callbacks, int mode)
    {
        this.mode = mode;
        setText(getButtonText());
        addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent e)
            {
                boolean configured = false;
                if(mode == 0)
                    configured = ConfigurationDialogs.showSource(view, getDialogMode());
                else if(mode == 1)
                    configured = ConfigurationDialogs.showJson(view, getDialogMode());
                boolean makeReqs = true;
                boolean completed = false;
                java.util.List<String> nodes = new ArrayList<>();
                if (configured)
                {
                    if (BurpPropertiesManager.getBurpPropertiesManager().getConfigFile() != null )
                        callbacks.loadConfigFromJson(getBurpConfigAsString());
                    try
                    {
                        EndpointDecorator[] endpoints = getEndpoints(view, false);
                        EndpointDecorator[] comparePoints = null;
                        if(BurpPropertiesManager.getBurpPropertiesManager().getOldSourceFolder()!= null && !BurpPropertiesManager.getBurpPropertiesManager().getOldSourceFolder().trim().isEmpty() && mode == 0)
                            comparePoints = getEndpoints(view, true);
                        else if(BurpPropertiesManager.getBurpPropertiesManager().getOldSerializationFile()!= null && !BurpPropertiesManager.getBurpPropertiesManager().getOldSerializationFile().trim().isEmpty() && mode == 1)
                            comparePoints = getEndpoints(view, true);
                        if (endpoints.length == 0)
                            JOptionPane.showMessageDialog(view, getNoEndpointsMessage(), "Warning", JOptionPane.WARNING_MESSAGE);
                        else
                        {
                            if (comparePoints != null && comparePoints.length != 0)
                                endpoints = compareEndpoints(endpoints, comparePoints, view);

                            fillEndpointsToTable(endpoints);
                            for (EndpointDecorator decorator : endpoints)
                            {
                                if (decorator != null)
                                {
                                    Endpoint.Info endpoint = decorator.getEndpoint();
                                    String endpointPath = endpoint.getUrlPath();
                                    if (endpointPath.startsWith("/"))
                                        endpointPath = endpointPath.substring(1);

                                    endpointPath = endpointPath.replaceAll(GENERIC_INT_SEGMENT, "1");
                                    nodes.add(endpointPath);
                                    for(Map.Entry<String, RouteParameter> parameter : endpoint.getParameters().entrySet())
                                        nodes.add(endpointPath + "?" + parameter.getKey() + "=" + parameter.getValue());
                                }
                            }
                            String url = UrlDialog.show(view);
                            if (url != null)
                            {
                                try
                                {
                                    if (!url.substring(url.length() - 1).equals("/"))
                                        url = url+"/";

                                    for (String node: nodes)
                                    {
                                        URL nodeUrl = new URL(url + node);
                                        callbacks.includeInScope(nodeUrl);
                                        if(BurpPropertiesManager.getBurpPropertiesManager().getAutoSpider())
                                            callbacks.sendToSpider(nodeUrl);
                                    }
                                    buildRequests(view, callbacks, endpoints, url);
                                    completed = true;
                                }
                                catch (MalformedURLException e1)
                                {
                                    JOptionPane.showMessageDialog(view, "Invalid URL.",
                                            "Warning", JOptionPane.WARNING_MESSAGE);
                                }

                                if (completed)
                                    JOptionPane.showMessageDialog(view, getCompletedMessage());
                            }
                            else
                                makeReqs = false;
                        }

                        if(makeReqs)
                        {
                            if (BurpPropertiesManager.getBurpPropertiesManager().getAutoScan())
                                sendToScanner(callbacks, UrlDialog.show(view));
                            RequestMakerThread rmt = new RequestMakerThread(callbacks, view);
                            new Thread(rmt).start();
                        }
                    }
                    catch(Exception ex)
                    {
                        JOptionPane.showMessageDialog(view, getErrorMessage());
                    }
                }
                else
                    JOptionPane.showMessageDialog(view, "The location of the source code to analyze is required to import endpoints, select the directory location in the plugin options", "Warning", JOptionPane.WARNING_MESSAGE);

            }
        });
    }

    private void sendToScanner(IBurpExtenderCallbacks callbacks, String url)
    {
        IHttpRequestResponse[] responses = callbacks.getSiteMap(url);
        for (IHttpRequestResponse response : responses)
        {
            IHttpService service = response.getHttpService();
            boolean useHttps = service.getProtocol().equalsIgnoreCase("https");
            callbacks.doActiveScan(service.getHost(), service.getPort(), useHttps, response.getRequest());
        }
    }

    private String getBurpConfigAsString()
    {
        try
        {
            JSONParser parser = new JSONParser();
            JSONObject jsonObject = (JSONObject) parser.parse(new FileReader(BurpPropertiesManager.getBurpPropertiesManager().getConfigFile()));
            return jsonObject.toJSONString();
        }
        catch (ParseException e)
        {
            e.printStackTrace();
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return "";
    }

    private void fillEndpointsToTable(EndpointDecorator[] decorators)
    {
        int count = 0;
        JTable endpointTable = BurpPropertiesManager.getBurpPropertiesManager().getEndpointsTable();
        DefaultTableModel dtm = (DefaultTableModel)endpointTable.getModel();
        while(dtm.getRowCount() > 0)
            dtm.removeRow(0);

        for (EndpointDecorator decorator : decorators)
        {
            Endpoint.Info endpoint = decorator.getEndpoint();
            boolean hasGet = false;
            boolean hasPost = false;
            String method = endpoint.getHttpMethod();
            if(method.toString().equalsIgnoreCase("post"))
                hasPost = true;
            else if (method.toString().equalsIgnoreCase("get"))
                hasGet = true;
            dtm.addRow(new Object[]{endpoint.getUrlPath(), endpoint.getParameters().size(), hasGet, hasPost,
                    (decorator.getStatus() == EndpointDecorator.Status.NEW || decorator.getStatus() == EndpointDecorator.Status.CHANGED), decorator});
            count++;
        }
        JLabel countLabel = BurpPropertiesManager.getBurpPropertiesManager().getCountLabel();
        countLabel.setVisible(true);
        countLabel.setText("Total Endpoints Detected: " + count);
    }

    private void buildRequests(final Component view, final IBurpExtenderCallbacks callbacks, EndpointDecorator[] decorators, String url) {
        HashMap<RequestDecorator, IHttpService> requests = new HashMap<RequestDecorator, IHttpService>();
        for (EndpointDecorator decorator : decorators)
        {
            Endpoint.Info endpoint = decorator.getEndpoint();
            if (endpoint != null)
            {
                String endpointPath = endpoint.getUrlPath();
                if (endpointPath.startsWith("/"))
                {
                    endpointPath = endpointPath.substring(1);
                }
                endpointPath = endpointPath.replaceAll(GENERIC_INT_SEGMENT, "1");

                boolean first = true;
                String reqString = endpointPath;
                String method = endpoint.getHttpMethod();
                try
                {
                    URL reqUrl = new URL(url + endpointPath);
                    for (Map.Entry<String, RouteParameter> parameter : endpoint.getParameters().entrySet())
                    {
                        if (first)
                        {
                            first = false;
                            reqString = reqString + "?";
                        }
                        else
                            reqString = reqString + "&";

                        if (parameter.getValue().getDataType() == ParameterDataType.STRING)
                            reqString = reqString + parameter.getKey() + "="+"debug";

                        else if (parameter.getValue().getDataType() == ParameterDataType.INTEGER)
                            reqString = reqString + parameter.getKey() + "="+"-1";

                        else if (parameter.getValue().getDataType() == ParameterDataType.BOOLEAN)
                            reqString = reqString + parameter.getKey() + "="+"true";

                        else if (parameter.getValue().getDataType() == ParameterDataType.DECIMAL)
                            reqString = reqString + parameter.getKey() + "="+".1";

                        else if (parameter.getValue().getDataType() == ParameterDataType.DATE_TIME)
                            reqString = reqString + parameter.getKey() + "="+ new Date();

                        else if (parameter.getValue().getDataType() == ParameterDataType.LOCAL_DATE)
                            reqString = reqString + parameter.getKey() + "="+new Date();

                    }
                    byte[] manReq = callbacks.getHelpers().buildHttpRequest(new URL(url + reqString));
                    if(method.toString().equalsIgnoreCase("requestmethod.post") || method.toString().equalsIgnoreCase("post"))
                        manReq = callbacks.getHelpers().toggleRequestMethod(manReq);

                    requests.put(new RequestDecorator(manReq, decorator.getStatus()), callbacks.getHelpers().buildHttpService(reqUrl.getHost(), reqUrl.getPort(), reqUrl.getProtocol()));
                }
                catch (MalformedURLException e1)
                {
                    JOptionPane.showMessageDialog(view, "Invalid URL.",
                            "Warning", JOptionPane.WARNING_MESSAGE);
                }
                catch (Exception ge)
                {
                    JOptionPane.showMessageDialog(view, ge.getMessage(),
                            "Warning", JOptionPane.WARNING_MESSAGE);
                }
            }
        }
        BurpPropertiesManager.getBurpPropertiesManager().setRequests(requests);
    }

    private EndpointDecorator[] compareEndpoints(EndpointDecorator[] decorators, EndpointDecorator[] comparePoints, final Component view)
    {
        for(EndpointDecorator decorator : decorators)
        {
            EndpointDecorator.Status newStat = EndpointDecorator.Status.NEW;
            for(EndpointDecorator comparePointDec : comparePoints)
            {
                if (decorator.getEndpoint().getUrlPath().equals(comparePointDec.getEndpoint().getUrlPath()) && decorator.getEndpoint().getHttpMethod().equals(comparePointDec.getEndpoint().getHttpMethod()))
                {
                    if (decorator.checkSum() != comparePointDec.checkSum())
                    {
                        newStat = EndpointDecorator.Status.CHANGED;
                        decorator.setComparePoint(comparePointDec.getEndpoint());
                        break;
                    }
                    else
                    {
                        newStat = EndpointDecorator.Status.UNCHANGED;
                        break;
                    }
                }
            }
            decorator.setStatus(newStat);
        }

        return decorators;
    }


    protected abstract String getButtonText();

    protected abstract String getNoEndpointsMessage();

    protected abstract String getCompletedMessage();

    protected abstract String getErrorMessage();

    protected abstract ConfigurationDialogs.DialogMode getDialogMode();

    protected abstract EndpointDecorator[] getEndpoints(final Component view, boolean compare);


}