package hudson.plugins.git.browser;

import hudson.EnvVars;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.TaskListener;
import hudson.plugins.git.GitChangeSet;
import hudson.plugins.git.GitChangeSet.Path;
import hudson.scm.RepositoryBrowser;

import org.apache.commons.validator.routines.UrlValidator;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;

import java.io.IOException;
import java.net.IDN;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

public abstract class GitRepositoryBrowser extends RepositoryBrowser<GitChangeSet> {

    private /* mostly final */ String url;

    @Deprecated
    protected GitRepositoryBrowser() {
    }

    protected GitRepositoryBrowser(String repourl) {
        this.url = repourl;
    }

    public final String getRepoUrl() {
        return url;
    }

    public final URL getUrl() throws IOException {
        String u = url;
        StaplerRequest req = Stapler.getCurrentRequest();
        if (req != null) {
            Job job = req.findAncestorObject(Job.class);
            if (job != null) {
                EnvVars env;
                try {
                    env = job.getEnvironment(null, TaskListener.NULL);
                } catch (InterruptedException e) {
                    throw new IOException("Failed to retrieve job environment", e);
                }
                u = env.expand(url);
            }
        }

        if (getNormalizeUrl()) {
            return normalizeToEndWithSlash(new URL(u));
        }
        else {
            return new URL(u);
        }
    }

    /**
     * Determines the link to the diff between the version
     * in the specified revision of {@link hudson.plugins.git.GitChangeSet.Path} to its previous version.
     *
     * @param path affected file path
     * @return
     *      null if the browser doesn't have any URL for diff.
     * @throws IOException on input or output error
     */
    public abstract URL getDiffLink(GitChangeSet.Path path) throws IOException;
    
    /**
     * Determines the link to a single file under Git.
     * This page should display all the past revisions of this file, etc.
     *
     * @param path affected file path
     * @return
     *      null if the browser doesn't have any suitable URL.
     * @throws IOException on input or output error
     * @throws URISyntaxException on URI syntax error
     */
    public abstract URL getFileLink(GitChangeSet.Path path) throws IOException, URISyntaxException;

    /**
     * Determines whether a URL should be normalized
	 * Overridden in the rare case where it shouldn't
     *
     * @return True if the URL should be normalized
     */
    protected boolean getNormalizeUrl() {
		return true;
	}
    
    /**
     * Calculate the index of the given path in a
     * sorted list of affected files
     *
     * @param path affected file path
     * @return The index in the lexicographical sorted filelist
     * @throws IOException on input or output error
     */
    protected int getIndexOfPath(Path path) throws IOException {
    	final String pathAsString = path.getPath();
    	final GitChangeSet changeSet = path.getChangeSet();
    	int i = 0;
    	for (String affected : changeSet.getAffectedPaths())
    	{
		if (affected.compareTo(pathAsString) < 0)
    			i++;
    	}
        return i;
    }

    public static URL encodeURL(URL url) throws IOException {
        try {
            return new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(), url.getQuery(), url.getRef()).toURL();
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }
    }

    protected static boolean initialChecksAndReturnOk(Item project, String cleanUrl){
        if (cleanUrl == null) {
            return true;
        }
        if (project == null || !project.hasPermission(Item.CONFIGURE)) {
            return true;
        }
        if (cleanUrl.contains("$")) {
            // set by variable, can't validate
            return true;
        }
        return false;
    }

    protected static boolean checkURIFormat(String url) throws URISyntaxException {
        String[] schemes = {"http", "https"};
        UrlValidator urlValidator = new UrlValidator(schemes);
        return urlValidator.isValid(url);
    }

    private static final long serialVersionUID = 1L;
}

@@ -257,24 +257,86 @@
 ryBrowser;%0A%0A
+import org.apache.commons.validator.routines.DomainValidator;%0A
 import org.a
@@ -588,16 +588,67 @@
 net.URL;
+%0Aimport java.util.ArrayList;%0Aimport java.util.List;
 %0A%0Apublic
@@ -4234,32 +4234,86 @@
 n false;%0A    %7D%0A%0A
+    /* Browser URL validation of remote/local urls */%0A
     protected st
@@ -4329,22 +4329,19 @@
 ean 
-checkURIFormat
+validateUrl
 (Str
@@ -4369,32 +4369,471 @@
 ntaxException %7B%0A
+        /* Any TLDs defined by IANA not included in the generic list can be added to this item list */%0A        DomainValidator.Item item = new DomainValidator.Item(DomainValidator.ArrayType.GENERIC_PLUS, new String%5B%5D %7B %22corp%22, %22home%22, %22local%22, %22localnet%22 %7D);%0A        List%3CDomainValidator.Item%3E itemList = new ArrayList%3C%3E();%0A        itemList.add(item);%0A        DomainValidator domainValidator = DomainValidator.getInstance(true, itemList);%0A
         String%5B%5D
@@ -4922,16 +4922,70 @@
 (schemes
+, null, UrlValidator.ALLOW_LOCAL_URLS, domainValidator
 );%0A     

Allow local hostnames to be validated by the UrlValidator