Config download methods

Allow download methods configuration in gerrit.config.  It was
included a section [download] in gerrit.config file to set the
scheme to be used in downloads.

This configuration hide or show ssh/http/anonymous git/anonymous
http and repo download tabs in change screen.

Bug: issue 583
Change-Id: I157135c6793771fc7b9e22bef5bae1a7d3b1da35
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 7384a87..4b7aead 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -713,6 +713,52 @@
 +
 Default is `30 seconds`.
 
+[[download]]Section download
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+----
+[download]
+  scheme = ssh
+  scheme = http
+  scheme = anon_http
+  scheme = anon_git
+  scheme = repo_download
+----
+
+The download section configures the allowed download methods.
+
+[[download.scheme]]download.scheme::
++
+Schemes that should be used to download changes.
++
+Multiple schemes are supported:
++
+* `http`
++
+HTTP download is allowed.
++
+* `ssh`
++
+SSH download is allowed.
++
+* `anon_http`
++
+Anonymous HTTP download is allowed.
++
+* `anon_git`
++
+Anonymous Git download is allowed.
+This is not default, it is also necessary to fill gerrit.canonicalGitUrl variable.
++
+* `repo_download`
++
+Gerrit advertises patch set downloads with the `repo download` command,
+assuming that all projects managed by this instance are generally worked
+on with the repo multi-repository tool.
+This is not default, as not all instances will deploy repo.
+
++
+If download.scheme is not specified, SSH, HTTP and Anonymous HTTP downloads are allowed.
 
 [[gerrit]]Section gerrit
 ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1267,17 +1313,6 @@
   safe = true
 ----
 
-[[repo]]Section repo
-~~~~~~~~~~~~~~~~~~~~
-
-[[repo.showDownloadCommand]]repo.showDownloadCommand::
-+
-If set to true, Gerrit advertises patch set downloads with the
-`repo download` command, assuming that all projects managed by this
-instance are generally worked on with the repo multi-repository tool.
-+
-By default, false, as not all instances will deploy repo.
-
 [[repository]]Section repository
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Repositories in this sense are the same as projects.
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
index c954a6e..2e6be7e 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.reviewdb.Account;
 import com.google.gerrit.reviewdb.AuthType;
 import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme;
 import com.google.gwtexpui.safehtml.client.RegexFindReplace;
 
 import java.util.List;
@@ -32,7 +33,7 @@
   protected boolean useContactInfo;
   protected boolean allowRegisterNewEmail;
   protected AuthType authType;
-  protected boolean useRepoDownload;
+  protected Set<DownloadScheme> downloadSchemes;
   protected String gitDaemonUrl;
   protected String sshdAddress;
   protected Project.NameKey wildProject;
@@ -71,6 +72,14 @@
     authType = t;
   }
 
+  public Set<DownloadScheme> getDownloadSchemes() {
+    return downloadSchemes;
+  }
+
+  public void setDownloadSchemes(final Set<DownloadScheme> s) {
+    downloadSchemes = s;
+  }
+
   public GitwebLink getGitwebLink() {
     return gitweb;
   }
@@ -95,14 +104,6 @@
     useContactInfo = r;
   }
 
-  public boolean isUseRepoDownload() {
-    return useRepoDownload;
-  }
-
-  public void setUseRepoDownload(final boolean r) {
-    useRepoDownload = r;
-  }
-
   public String getGitDaemonUrl() {
     return gitDaemonUrl;
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandLink.java
index 1260819..53771a97 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandLink.java
@@ -62,6 +62,10 @@
     }
   }
 
+  public AccountGeneralPreferences.DownloadCommand getCmdType() {
+    return cmdType;
+  }
+
   void select() {
     DownloadCommandPanel parent = (DownloadCommandPanel) getParent();
     for (Widget w : parent) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandPanel.java
index 3ecf527..0affcfb 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadCommandPanel.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.reviewdb.AccountGeneralPreferences;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadCommand;
 import com.google.gwt.user.client.ui.Accessibility;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.Widget;
@@ -69,6 +70,9 @@
   private void update() {
     if (currentCommand != null && currentUrl != null) {
       currentCommand.setCurrentUrl(currentUrl);
+    } else if (currentCommand != null &&
+        currentCommand.getCmdType().equals(DownloadCommand.REPO_DOWNLOAD)) {
+      currentCommand.setCurrentUrl(null);
     }
   }
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlLink.java
index 6f52ef9..6e72e20 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlLink.java
@@ -25,10 +25,10 @@
 import com.google.gwtjsonrpc.client.VoidResult;
 
 class DownloadUrlLink extends Anchor implements ClickHandler {
-  final AccountGeneralPreferences.DownloadUrl urlType;
+  final AccountGeneralPreferences.DownloadScheme urlType;
   final String urlData;
 
-  DownloadUrlLink(AccountGeneralPreferences.DownloadUrl urlType, String text,
+  DownloadUrlLink(AccountGeneralPreferences.DownloadScheme urlType, String text,
       String urlData) {
     super(text);
     this.urlType = urlType;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlPanel.java
index 783963b..f4260e2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlPanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/DownloadUrlPanel.java
@@ -33,7 +33,7 @@
     return getWidgetCount() == 0;
   }
 
-  void select(AccountGeneralPreferences.DownloadUrl urlType) {
+  void select(AccountGeneralPreferences.DownloadScheme urlType) {
     DownloadUrlLink first = null;
 
     for (Widget w : this) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
index da4b78b..ce0cb6d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -37,7 +37,7 @@
 import com.google.gerrit.reviewdb.Project;
 import com.google.gerrit.reviewdb.UserIdentity;
 import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadCommand;
-import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadUrl;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
@@ -175,34 +175,40 @@
     final CopyableLabel copyLabel = new CopyableLabel("");
     final DownloadCommandPanel commands = new DownloadCommandPanel();
     final DownloadUrlPanel urls = new DownloadUrlPanel(commands);
+    final Set<DownloadScheme> allowedSchemes = Gerrit.getConfig().getDownloadSchemes();
 
     copyLabel.setStyleName(Gerrit.RESOURCES.css().downloadLinkCopyLabel());
 
     if (changeDetail.isAllowsAnonymous()
-        && Gerrit.getConfig().getGitDaemonUrl() != null) {
+        && Gerrit.getConfig().getGitDaemonUrl() != null
+        && allowedSchemes.contains(DownloadScheme.ANON_GIT)) {
       StringBuilder r = new StringBuilder();
       r.append(Gerrit.getConfig().getGitDaemonUrl());
       r.append(projectName);
       r.append(" ");
       r.append(patchSet.getRefName());
-      urls.add(new DownloadUrlLink(DownloadUrl.ANON_GIT, Util.M
+      urls.add(new DownloadUrlLink(DownloadScheme.ANON_GIT, Util.M
           .anonymousDownload("Git"), r.toString()));
     }
 
-    if (changeDetail.isAllowsAnonymous()) {
+    if (changeDetail.isAllowsAnonymous()
+        && (allowedSchemes.contains(DownloadScheme.ANON_HTTP) ||
+            allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) {
       StringBuilder r = new StringBuilder();
       r.append(GWT.getHostPageBaseURL());
       r.append("p/");
       r.append(projectName);
       r.append(" ");
       r.append(patchSet.getRefName());
-      urls.add(new DownloadUrlLink(DownloadUrl.ANON_HTTP, Util.M
+      urls.add(new DownloadUrlLink(DownloadScheme.ANON_HTTP, Util.M
           .anonymousDownload("HTTP"), r.toString()));
     }
 
     if (Gerrit.getConfig().getSshdAddress() != null && Gerrit.isSignedIn()
         && Gerrit.getUserAccount().getUserName() != null
-        && Gerrit.getUserAccount().getUserName().length() > 0) {
+        && Gerrit.getUserAccount().getUserName().length() > 0
+        && (allowedSchemes.contains(DownloadScheme.SSH) ||
+            allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) {
       String sshAddr = Gerrit.getConfig().getSshdAddress();
       final StringBuilder r = new StringBuilder();
       r.append("ssh://");
@@ -219,11 +225,13 @@
       r.append(projectName);
       r.append(" ");
       r.append(patchSet.getRefName());
-      urls.add(new DownloadUrlLink(DownloadUrl.SSH, "SSH", r.toString()));
+      urls.add(new DownloadUrlLink(DownloadScheme.SSH, "SSH", r.toString()));
     }
 
     if (Gerrit.isSignedIn() && Gerrit.getUserAccount().getUserName() != null
-        && Gerrit.getUserAccount().getUserName().length() > 0) {
+        && Gerrit.getUserAccount().getUserName().length() > 0
+        && (allowedSchemes.contains(DownloadScheme.HTTP) ||
+            allowedSchemes.contains(DownloadScheme.DEFAULT_DOWNLOADS))) {
       String base = GWT.getHostPageBaseURL();
       int p = base.indexOf("://");
       int s = base.indexOf('/', p + 3);
@@ -245,10 +253,10 @@
       r.append(projectName);
       r.append(" ");
       r.append(patchSet.getRefName());
-      urls.add(new DownloadUrlLink(DownloadUrl.HTTP, "HTTP", r.toString()));
+      urls.add(new DownloadUrlLink(DownloadScheme.HTTP, "HTTP", r.toString()));
     }
 
-    if (Gerrit.getConfig().isUseRepoDownload()) {
+    if (allowedSchemes.contains(DownloadScheme.REPO_DOWNLOAD)) {
       // This site prefers usage of the 'repo' tool, so suggest
       // that for easy fetch.
       //
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
index e075ed8..e51731d 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.reviewdb.Project;
 import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.config.DownloadSchemeConfig;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.WildProjectName;
 import com.google.gerrit.server.contact.ContactStore;
@@ -47,6 +48,7 @@
   private final Realm realm;
   private final Config cfg;
   private final AuthConfig authConfig;
+  private final DownloadSchemeConfig schemeConfig;
   private final GitWebConfig gitWebConfig;
   private final Project.NameKey wildProject;
   private final SshInfo sshInfo;
@@ -60,10 +62,12 @@
   GerritConfigProvider(final Realm r, @GerritServerConfig final Config gsc,
       final AuthConfig ac, final GitWebConfig gwc,
       @WildProjectName final Project.NameKey wp, final SshInfo si,
-      final ApprovalTypes at, final ContactStore cs, final ServletContext sc) {
+      final ApprovalTypes at, final ContactStore cs, final ServletContext sc,
+      final DownloadSchemeConfig dc) {
     realm = r;
     cfg = gsc;
     authConfig = ac;
+    schemeConfig = dc;
     gitWebConfig = gwc;
     sshInfo = si;
     wildProject = wp;
@@ -92,9 +96,8 @@
     config.setUseContributorAgreements(cfg.getBoolean("auth",
         "contributoragreements", false));
     config.setGitDaemonUrl(cfg.getString("gerrit", null, "canonicalgiturl"));
-    config.setUseRepoDownload(cfg.getBoolean("repo", null,
-        "showdownloadcommand", false));
     config.setUseContactInfo(contactStore != null && contactStore.isEnabled());
+    config.setDownloadSchemes(schemeConfig.getDownloadScheme());
     config.setAuthType(authConfig.getAuthType());
     config.setWildProject(wildProject);
     config.setApprovalTypes(approvalTypes);
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGeneralPreferences.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGeneralPreferences.java
index 9b607b0..c69e785 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGeneralPreferences.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGeneralPreferences.java
@@ -25,9 +25,9 @@
   /** Valid choices for the page size. */
   public static final short[] PAGESIZE_CHOICES = {10, 25, 50, 100};
 
-  /** Preferred URL type to download a change. */
-  public static enum DownloadUrl {
-    ANON_GIT, ANON_HTTP, ANON_SSH, HTTP, SSH;
+  /** Preferred scheme type to download a change. */
+  public static enum DownloadScheme {
+    ANON_GIT, ANON_HTTP, ANON_SSH, HTTP, SSH, REPO_DOWNLOAD, DEFAULT_DOWNLOADS;
   }
 
   /** Preferred method to download a change. */
@@ -86,14 +86,14 @@
     useFlashClipboard = b;
   }
 
-  public DownloadUrl getDownloadUrl() {
+  public DownloadScheme getDownloadUrl() {
     if (downloadUrl == null) {
       return null;
     }
-    return DownloadUrl.valueOf(downloadUrl);
+    return DownloadScheme.valueOf(downloadUrl);
   }
 
-  public void setDownloadUrl(DownloadUrl url) {
+  public void setDownloadUrl(DownloadScheme url) {
     if (url != null) {
       downloadUrl = url.name();
     } else {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
index f889a2b..73320ad 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
@@ -26,7 +26,9 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.text.MessageFormat;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
@@ -43,13 +45,17 @@
    *        Must not be null as the enumeration values are derived from this.
    * @return the selected enumeration value, or {@code defaultValue}.
    */
-  @SuppressWarnings("unchecked")
   public static <T extends Enum<?>> T getEnum(final Config config,
       final String section, final String subsection, final String setting,
       final T defaultValue) {
-    final T[] all;
+    final T[] all = allValuesOf(defaultValue);
+    return getEnum(config, section, subsection, setting, all, defaultValue);
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <T> T[] allValuesOf(final T defaultValue) {
     try {
-      all = (T[]) defaultValue.getClass().getMethod("values").invoke(null);
+      return (T[]) defaultValue.getClass().getMethod("values").invoke(null);
     } catch (IllegalArgumentException e) {
       throw new IllegalArgumentException("Cannot obtain enumeration values", e);
     } catch (SecurityException e) {
@@ -61,7 +67,6 @@
     } catch (NoSuchMethodException e) {
       throw new IllegalArgumentException("Cannot obtain enumeration values", e);
     }
-    return getEnum(config, section, subsection, setting, all, defaultValue);
   }
 
   /**
@@ -86,6 +91,25 @@
       return defaultValue;
     }
 
+    return getEnum(section, subsection, setting, valueString, all);
+  }
+
+  /**
+   * Parse a Java enumeration from the configuration.
+   *
+   * @param <T> type of the enumeration object.
+   * @param section section the key is in.
+   * @param subsection subsection the key is in, or null if not in a subsection.
+   * @param setting name of the setting to read.
+   * @param valueString string value from git Config
+   * @param all all possible values in the enumeration which should be
+   *        recognized. This should be {@code EnumType.values()}.
+   * @return the selected enumeration value, or {@code defaultValue}.
+   */
+  private static <T extends Enum<?>> T getEnum(final String section,
+      final String subsection, final String setting, String valueString,
+      final T[] all) {
+
     String n = valueString.replace(' ', '_');
     for (final T e : all) {
       if (equalsIgnoreCase(e.name(), n)) {
@@ -109,10 +133,62 @@
       r.append(e.name());
       r.append(" ");
     }
+
     throw new IllegalArgumentException(r.toString().trim());
   }
 
   /**
+   * Parse a Java enumeration list from the configuration.
+   *
+   * @param <T> type of the enumeration object.
+   * @param config the configuration file to read.
+   * @param section section the key is in.
+   * @param subsection subsection the key is in, or null if not in a subsection.
+   * @param setting name of the setting to read.
+   * @param defaultValue default value to return if the setting was not set.
+   *        Must not be null as the enumeration values are derived from this.
+   * @return the selected enumeration values list, or {@code defaultValue}.
+   */
+  @SuppressWarnings("unchecked")
+  public static <T extends Enum<?>> List<T> getEnumList(final Config config,
+      final String section, final String subsection, final String setting,
+      final T defaultValue) {
+    final T[] all = allValuesOf(defaultValue);
+    return getEnumList(config, section, subsection, setting, all, defaultValue);
+  }
+
+  /**
+   * Parse a Java enumeration list from the configuration.
+   *
+   * @param <T> type of the enumeration object.
+   * @param config the configuration file to read.
+   * @param section section the key is in.
+   * @param subsection subsection the key is in, or null if not in a subsection.
+   * @param setting name of the setting to read.
+   * @param all all possible values in the enumeration which should be
+   *        recognized. This should be {@code EnumType.values()}.
+   * @param defaultValue default value to return if the setting was not set.
+   *        This value may be null.
+   * @return the selected enumeration values list, or {@code defaultValue}.
+   */
+  public static <T extends Enum<?>> List<T> getEnumList(final Config config,
+      final String section, final String subsection, final String setting,
+      final T[] all, final T defaultValue) {
+    final List<T> list = new ArrayList<T>();
+    final String[] values = config.getStringList(section, subsection, setting);
+    if (values.length == 0) {
+      list.add(defaultValue);
+    } else {
+      for (String string : values) {
+        if (string != null) {
+          list.add(getEnum(section, subsection, setting, string, all));
+        }
+      }
+    }
+    return list;
+  }
+
+  /**
    * Parse a numerical time unit, such as "1 minute", from the configuration.
    *
    * @param config the configuration file to read.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/DownloadSchemeConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/DownloadSchemeConfig.java
new file mode 100644
index 0000000..6f67032
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/DownloadSchemeConfig.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2010 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.google.gerrit.server.config;
+
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.lib.Config;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/** Download protocol from {@code gerrit.config}. */
+@Singleton
+public class DownloadSchemeConfig {
+  private final Set<DownloadScheme> downloadSchemes;
+
+  @Inject
+  DownloadSchemeConfig(@GerritServerConfig final Config cfg,
+      final SystemConfig s) {
+    List<DownloadScheme> all =
+        ConfigUtil.getEnumList(cfg, "download", null, "scheme",
+            DownloadScheme.DEFAULT_DOWNLOADS);
+
+    downloadSchemes =
+        Collections.unmodifiableSet(new HashSet<DownloadScheme>(all));
+  }
+
+  /** Scheme used to download. */
+  public Set<DownloadScheme> getDownloadScheme() {
+    return downloadSchemes;
+  }
+}