// 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.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.reviewdb.client.AuthType;
import com.google.gerrit.server.ssh.SshAddressesModule;
public class GitBlitUrlsConfig {
private static final Logger log = LoggerFactory.getLogger(GitBlitUrlsConfig.class);
private static final String GITBLIT_REPO = "{0}";
private static final String GITBLIT_USER = "{1}";
private final String canonicalWebUrlString;
private final String sshGitUrl;
private final String httpdListenUrlString;
private final String loginUrl;
private final List<String> downloadSchemes;
public GitBlitUrlsConfig(Config config, List<SocketAddress> sshListenAddresses, List<String> sshAdvertizedAddresses) {
canonicalWebUrlString = config.getString("gerrit", null, "canonicalWebUrl");
httpdListenUrlString = config.getString("httpd", null, "listenUrl");
downloadSchemes = Arrays.asList(config.getStringList("download", null, "scheme"));
sshGitUrl = determineGitSshUrl(sshListenAddresses, sshAdvertizedAddresses);
AuthType gerritAuthType = null;
try {
gerritAuthType = config.getEnum("auth", null, "type", AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT);
} catch (IllegalArgumentException ex) {
// Swallow; handled below.
}
String loginVia = null;
if (!ImmutableSet.of(AuthType.LDAP, AuthType.LDAP_BIND).contains(gerritAuthType)) {
loginVia = canonicalWebUrlString + (canonicalWebUrlString.endsWith("/") ? "" : "/") + "login/";
}
loginUrl = loginVia;
}
/**
* Gets the login Url to use for GitBlit. If non-null, the plugin will only display a "Log in" link going to this Url instead of GitBlit's normal
* username-password login form. This makes it possible to use the plugin with an external authentication provider as configured for Gerrit.
*
* @return the login Url to use, if any, or {@code null} if GitBlit's normal login form shall be used (for instance, if Gerrit uses LDAP for
* authentication).
*/
public String getLoginUrl() {
return loginUrl;
}
/**
* Gets Gerrit's canonical web URL.
*
* @return the Url
*/
public String getCanonicalWebUrl() {
return canonicalWebUrlString;
}
private String determineGitSshUrl(List<SocketAddress> sshListenAddresses, List<String> sshAdvertizedAddresses) {
if (sshListenAddresses == null || sshListenAddresses.isEmpty()) {
return null;
}
if (!downloadSchemes.isEmpty() && !downloadSchemes.contains("ssh")) {
return null;
}
String sshUrl = null;
for (String candidate : sshAdvertizedAddresses) {
sshUrl = getSshUrl(candidate);
if (sshUrl != null) {
break;
}
}
if (sshUrl == null && !sshListenAddresses.isEmpty()) {
SocketAddress s = sshListenAddresses.get(0);
if (s instanceof InetSocketAddress) {
String host = ((InetSocketAddress) s).getHostString();
int port = ((InetSocketAddress) s).getPort();
sshUrl = formatUrl(host, port);
}
}
if (sshUrl == null) {
log.error("Cannot determine ssh clone URL");
}
return sshUrl;
}
private String getSshUrl(String candidate) {
String[] parts = candidate.split(":");
String host;
int port = SshAddressesModule.IANA_SSH_PORT;
try {
switch (parts.length) {
case 1:
// No port
host = getHost(parts[0]);
break;
case 2:
host = getHost(parts[0]);
port = getPort(parts[1]);
break;
default:
log.error("Invalid sshd advertised URL: " + candidate);
return null;
}
return formatUrl(host, port);
} catch (UnknownHostException e) {
log.error("Cannot detect localhostname");
} catch (NumberFormatException ex) {
log.error("Invalid port number in sshd address: " + candidate);
}
return null;
}
private String formatUrl(String host, int port) {
return "ssh://" + GITBLIT_USER + '@' + host + (port == SshAddressesModule.IANA_SSH_PORT ? "" : ":" + port) + '/' + GITBLIT_REPO;
}
public String getGitSshUrl() {
return sshGitUrl == null ? "" : sshGitUrl;
}
private int getPort(String port) {
int portNumber = Integer.parseInt(port);
if (portNumber <= 0) {
throw new NumberFormatException();
}
return portNumber;
}
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 "";
}
if (!downloadSchemes.isEmpty() && !downloadSchemes.contains("http")) {
return "";
}
String httpUrl = canonicalWebUrlString == null ? httpListenUrl : canonicalWebUrlString;
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;
}
}