web.otherUrls auto-config based on Gerrit SSH/HTTPD URLs.

Allow GitBlit to automatically inherit the Gerrit SSH
and HTTPD URLs configuration into its web.otherUrls
settings.

This allows to have in GitBlit the "repository URL"
fields correctly populated and customised on a per
user and per repo basis.

Additionally the URLs, Git, SourceTree and Tower
client buttons will be automatically populated and
working out-of-the-box.

Change-Id: I447768919039e1b3737c7137ce8396ff86b91a1d
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GerritWicketFilter.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GerritWicketFilter.java
index dc42da1..c543666 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GerritWicketFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GerritWicketFilter.java
@@ -15,6 +15,8 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -30,13 +32,16 @@
 import javax.servlet.ServletResponse;
 
 import org.apache.wicket.protocol.http.WicketFilter;
+import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants;
 import com.gitblit.GitBlit;
 import com.gitblit.IStoredSettings;
+import com.google.gerrit.common.data.GerritConfig;
 import com.google.gerrit.httpd.WebSession;
+import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.LocalDiskRepositoryManager;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -50,7 +55,6 @@
 @Singleton
 public class GerritWicketFilter extends WicketFilter {
   private static final String GITBLIT_GERRIT_PROPERTIES = "/gitblit.properties";
-
   private static final Logger log = LoggerFactory
       .getLogger(GerritWicketFilter.class);
 
@@ -60,16 +64,19 @@
   // We need Guice to create the GerritGitBlit instance
   private final GerritGitBlit gitBlit;
   private final GerritAuthFilter gerritAuthFilter;
+  private final GitBlitUrlsConfig config;
 
   @Inject
   public GerritWicketFilter(final LocalDiskRepositoryManager repoManager,
       final Provider<WebSession> webSession, final GerritGitBlit gitBlit,
-      final GerritAuthFilter gerritAuthFilter) {
+      final GerritAuthFilter gerritAuthFilter, final @GerritServerConfig Config config,
+      final GerritConfig gerritConfig) {
 
     this.repoManager = repoManager;
     this.webSession = webSession;
     this.gitBlit = gitBlit;
     this.gerritAuthFilter = gerritAuthFilter;
+    this.config = new GitBlitUrlsConfig(config);
   }
 
   @Override
@@ -87,6 +94,8 @@
             .getAbsolutePath());
         properties.put("realm.userService",
             GerritToGitBlitUserService.class.getName());
+        properties.put("web.otherUrls",
+            (config.getGitHttpUrl() + " " + config.getGitSshUrl()).trim());
       } finally {
         resin.close();
       }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitUrlsConfig.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitUrlsConfig.java
new file mode 100644
index 0000000..8367e79
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/GitBlitUrlsConfig.java
@@ -0,0 +1,109 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed 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 com.googlesource.gerrit.plugins.gitblit;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+
+public class GitBlitUrlsConfig {
+  private static final int SSH_DEF_PORT = 22;
+  private static final String GITBLIT_REPO = "{0}";
+  private static final String GITBLIT_USER = "{1}";
+  private static final Logger log = LoggerFactory
+      .getLogger(GitBlitUrlsConfig.class);
+
+  private String canonicalWebUrlString;
+  private String sshdListenAddressString;
+  private String httpdListenUrlString;
+
+  public GitBlitUrlsConfig(Config config) {
+    canonicalWebUrlString = config.getString("gerrit", null, "canonicalWebUrl");
+    sshdListenAddressString = config.getString("sshd", null, "listenAddress");
+    httpdListenUrlString = config.getString("httpd", null, "listenUrl");
+  }
+
+  public String getGitSshUrl() {
+    if (sshdListenAddressString == null) {
+      return "";
+    }
+    String[] urlParts = sshdListenAddressString.split(":");
+    if (urlParts.length < 2) {
+      log.error("Invalid SSHD listenUrl: " + sshdListenAddressString);
+      return "";
+    }
+    try {
+      String hostname = getHost(urlParts[0]);
+      int port = getPort(urlParts[1]);
+
+      return "ssh://" + GITBLIT_USER + "@" + hostname
+          + (port == SSH_DEF_PORT ? "" : ":" + port) + "/" + GITBLIT_REPO + "";
+    } catch (UnknownHostException e) {
+      log.error("Cannot detect localhostname");
+      return "";
+    }
+  }
+
+  private int getPort(String port) {
+    return Integer.parseInt(port);
+  }
+
+  private String getHost(String hostname) throws UnknownHostException {
+    if (hostname.equals("*")) {
+      try {
+        if (canonicalWebUrlString != null) {
+          return new URI(canonicalWebUrlString).getHost();
+        }
+      } catch (URISyntaxException e) {
+        log.error("Cannot parse canonicalWebUrl and get external hostname,"
+            + " fallback to auto-detected local hostname", e);
+      }
+      return InetAddress.getLocalHost().getCanonicalHostName();
+    } else {
+      return hostname;
+    }
+  }
+
+  public String getGitHttpUrl() throws UnknownHostException {
+    String httpListenUrl = getHttpListenUrl();
+    if (httpListenUrl == null) {
+      return "";
+    }
+
+    String httpUrl = Objects.firstNonNull(canonicalWebUrlString, httpListenUrl);
+    httpUrl = httpUrl.replace("://", "://" + GITBLIT_USER + "@");
+    httpUrl += (httpUrl.endsWith("/") ? "" : "/") + GITBLIT_REPO;
+    return httpUrl;
+  }
+
+  private String getHttpListenUrl() throws UnknownHostException {
+    if (httpdListenUrlString == null) {
+      return null;
+    }
+    String url = httpdListenUrlString.replaceFirst("proxy-", "");
+    if (url.indexOf('*') > 0) {
+      url =
+          url.replaceFirst("\\*", InetAddress.getLocalHost()
+              .getCanonicalHostName());
+    }
+    return url;
+  }
+}