package net.sourceforge.jnlp.security.appletextendedsecurity;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import static org.junit.Assert.assertEquals;

import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import net.sourceforge.jnlp.InformationDesc;
import net.sourceforge.jnlp.ServerAccess;
import net.sourceforge.jnlp.browsertesting.browsers.firefox.FirefoxProfilesOperator;
import net.sourceforge.jnlp.mock.DummyJNLPFileWithJar;
import net.sourceforge.jnlp.security.appletextendedsecurity.impl.UnsignedAppletActionStorageImpl;
import net.sourceforge.jnlp.security.dialogs.apptrustwarningpanel.UnsignedAppletTrustWarningPanel;
import net.sourceforge.jnlp.util.FileUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;

import org.junit.Test;

public class UnsignedAppletTrustConfirmationTest {

    private static final String surl1 = "http://codeba.se/app";
    private static final String url41 = "http://my.url/app/";
    private static final String url42 = "resource.jar";
    private static URL url;
    private static URL url4;
    
    private static final File trustFile = new File(System.getProperty("user.home") + "/.config/icedtea-web/.appletTrustSettings");

    private static class DummyJnlpWithTitleAndUrls extends DummyJNLPFileWithJar {

        public DummyJnlpWithTitleAndUrls(URL u) throws MalformedURLException {
            super(url, u);
        }

        @Override
        public InformationDesc getInformation() {
            return new InformationDesc(null) {

                @Override
                public String getTitle() {
                    return "Demo App";
                }

            };
        }

        @Override
        public URL getCodeBase() {
            return url;
        }

        @Override
        public URL getSourceLocation() {
            return url;
        }

    };
    
    @BeforeClass
    public static void initUrl() throws MalformedURLException {
        url=new URL(surl1);
        url4=new URL(url41+url42);
    }
   
   private static File backup;

    @BeforeClass
    public static void backupAppTrust() throws IOException{
        backup = File.createTempFile("appletExtendedSecurity", "itwUnittest");
        backup.deleteOnExit();
        FirefoxProfilesOperator.copyFile(trustFile, backup);
    }
    
    @AfterClass
    public static void restoreAppTrust() throws IOException{
        FirefoxProfilesOperator.copyFile(backup, trustFile);
    }

    @Test
    public void updateAppletActionTest1() throws Exception {
        trustFile.delete(); //clean file to examine later
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrls(url4),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        String s = FileUtils.loadFileAsString(trustFile);
        s = s.replaceAll("#.*\n", ""); 
        Assert.assertTrue(s.startsWith("A"));
        Assert.assertTrue(s.contains(url41+url42));
        Assert.assertTrue(s.contains(surl1));
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrls(url4),
                ExecuteAppletAction.NEVER,
                Boolean.TRUE);
        s = FileUtils.loadFileAsString(trustFile);
        s = s.replaceAll("#.*\n", "");
        Assert.assertTrue(s.startsWith("N"));
        Assert.assertFalse(s.contains(url41+url42));
        Assert.assertTrue(s.contains(surl1));        
    }

    @Test
    public void testToRelativePaths() throws Exception {
        /* Absolute -> Relative */
        assertEquals(Arrays.asList("test.jar"),
                UnsignedAppletTrustConfirmation.toRelativePaths(Arrays.asList("http://example.com/test.jar"), "http://example.com/"));

        /* Relative is unchanged */
        assertEquals(Arrays.asList("test.jar"),
                UnsignedAppletTrustConfirmation.toRelativePaths(Arrays.asList("test.jar"), "http://example.com/"));

        /* Different root URL is unchanged */
        assertEquals(Arrays.asList("http://example2.com/test.jar"),
                UnsignedAppletTrustConfirmation.toRelativePaths(Arrays.asList("http://example2.com/test.jar"), "http://example.com/"));

        /* Path with invalid URL characters is handled */
        assertEquals(Arrays.asList("test .jar"),
                UnsignedAppletTrustConfirmation.toRelativePaths(Arrays.asList("http://example.com/test .jar"), "http://example.com/"));
    }
    
    
    @Test
    public void testSripFile() throws Exception {
        String sample = "http://aa.bb/";
        String result = UnsignedAppletTrustConfirmation.stripFile(new URL(sample));
        assertEquals(sample, result);
        sample = "http://aa.bb";
        result = UnsignedAppletTrustConfirmation.stripFile(new URL(sample));
        assertEquals(sample + "/", result);
        sample = "http://aa.bb/";
        result = UnsignedAppletTrustConfirmation.stripFile(new URL(sample + "cc"));
        assertEquals(sample, result);
        sample = "http://aa.bb/cc/";
        result = UnsignedAppletTrustConfirmation.stripFile(new URL(sample));
        assertEquals(sample, result);
        sample = "http://aa.bb/some/complicated/";
        result = UnsignedAppletTrustConfirmation.stripFile(new URL(sample + "some"));
        assertEquals(sample, result);
        sample = "http://aa.bb/some/complicated/some/";
        result = UnsignedAppletTrustConfirmation.stripFile(new URL(sample));
        assertEquals(sample, result);
        sample = "http://aa.bb/some/";
        result = UnsignedAppletTrustConfirmation.stripFile(new URL(sample + "strange?a=b"));
        assertEquals(sample, result);
        sample = "http://aa.bb/some/strange/";
        result = UnsignedAppletTrustConfirmation.stripFile(new URL(sample + "?a=b"));
        assertEquals(sample, result);
        
    }
    
    
    private static URL urlX1;
    private static URL urlX2;
    private static URL urlX3;
    
    private static URL urlY1;
    private static URL urlY2;
    private static URL urlY3;
    private static URL urlY4;
    private static URL urlY5;
    private static URL urlY6;
    private static URL urlY7;
    private static URL urlY8;
    
    @BeforeClass
    public static void initUrlsX123() throws MalformedURLException, IOException {
        urlX1 = new URL("http://&#10;does&#32;not&#32;metter&#32;is&#32;ok");
        urlX2 = new URL("http://\ndoes not metter is harmfull");
        Properties p = new Properties();
        p.load(new StringReader("key=http:\\u002F\\u002F\\u000Adoes\\u0020not\\u0020metter\\u0020is\\u0020harmfull"));
        urlX3=new URL(p.getProperty("key"));
    }

    @BeforeClass
    public static void initUrlsY12345678() throws MalformedURLException, IOException {
        urlY1 = new URL("http://som\\EeUrl.cz/aa");
        urlY2 = new URL("http://some\\QUrl.cz/aa");
        urlY3 = new URL("http://so\\QmeU\\Erl.cz/aa");
        urlY4 = new URL("http://so\\EmeU\\Qrl.cz/aa");

        urlY5 = new URL("http://someUrl.cz/aa\\Ebb/cc");
        urlY6 = new URL("http://someUrl.cz/aa\\Qbb/cc");
        urlY7 = new URL("http://someUrl.cz/aa\\Qbb/cc/dd\\Eee");
        urlY8 = new URL("http://someUrl.cz/aa\\Ebb/cc/dd\\Qee");
    }

    @Test
    public void updateAppletActionTestYQN1234saveAndLoadFine() throws Exception {
        trustFile.delete(); //clean file to examine later
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrlsWithOverwrite(urlY1),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrlsWithOverwrite(urlY2),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrlsWithOverwrite(urlY3),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrlsWithOverwrite(urlY4),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        AppletStartupSecuritySettings securitySettings = AppletStartupSecuritySettings.getInstance();
        UnsignedAppletActionStorageImpl userActionStorage = (UnsignedAppletActionStorageImpl) securitySettings.getUnsignedAppletActionCustomStorage();
        List<UnsignedAppletActionEntry> ll = userActionStorage.getMatchingItems(null, null, null);
        Assert.assertEquals(4, ll.size());
    }

    @Test
    public void updateAppletActionTestYQN5678saveAndLoadFine() throws Exception {
        trustFile.delete(); //clean file to examine later
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrlsWithOverwrite(urlY5),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrlsWithOverwrite(urlY6),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrlsWithOverwrite(urlY7),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrlsWithOverwrite(urlY8),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        AppletStartupSecuritySettings securitySettings = AppletStartupSecuritySettings.getInstance();
        UnsignedAppletActionStorageImpl userActionStorage = (UnsignedAppletActionStorageImpl) securitySettings.getUnsignedAppletActionCustomStorage();
        List<UnsignedAppletActionEntry> ll = userActionStorage.getMatchingItems(null, null, null);
        Assert.assertEquals(4, ll.size());
    }
    
    @Test
    public void updateAppletActionTestX3() throws Exception {
        trustFile.delete(); //clean file to examine later
        try{
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrls(urlX3),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        //may throw  RuntimeExeption which is correct, however, wee need to check result
        } catch (Exception ex){
            ServerAccess.logException(ex);
        }
        String s = FileUtils.loadFileAsString(trustFile);
        Assert.assertFalse(s.contains("harmfull"));
    }
    
    @Test
    public void updateAppletActionTestX2() throws Exception {
        trustFile.delete(); //clean file to examine later
        try{
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrls(urlX2),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        //may throw  RuntimeExeption which is correct, however, wee need to check result
        } catch (Exception ex){
            ServerAccess.logException(ex);
        }
        String s = FileUtils.loadFileAsString(trustFile);
        Assert.assertFalse(s.contains("harmfull"));
    }
    
     @Test
    public void updateAppletActionTestX1() throws Exception {
        //this case is correct, if html ecnoded url is passed as URL from javaws, it is kept intact
        trustFile.delete(); //clean file to examine later
        Exception eex = null;
        try{
        UnsignedAppletTrustConfirmation.updateAppletAction(
                new DummyJnlpWithTitleAndUrls(urlX1),
                ExecuteAppletAction.ALWAYS,
                Boolean.FALSE);
        //may throw  RuntimeExeption which is correct, however, wee need to check result
        } catch (Exception ex){
            eex = ex;
            ServerAccess.logException(ex);
        }
        String s = FileUtils.loadFileAsString(trustFile);
        Assert.assertNull(eex);
        Assert.assertTrue(s.contains("http://&#10;does&#32;not&#32;metter&#32;is&#32;ok"));
    }

    private static class DummyJnlpWithTitleAndUrlsWithOverwrite extends DummyJnlpWithTitleAndUrls {
        private final URL u;

        public DummyJnlpWithTitleAndUrlsWithOverwrite(URL u) throws MalformedURLException {
            super(u);
            this.u  = u;
        }

        @Override
        public URL getCodeBase() {
            return u;
        }

            @Override
            public URL getSourceLocation() {
                return u;
            }
    }
}
