Merge branch 'stable-3.1'

* stable-3.1:
  ChangeEditModifier: Reject invalid file paths as '400 Bad Request'
  Remove orphan library downloader facility
  Fix a bug where _moreChanges is set to the wrong value
  Don't check conflicting ref names when deleting refs

Change-Id: I3eb1d9d4821a1f925ce05f3809e18118e626e419
diff --git a/java/com/google/gerrit/git/RefUpdateUtil.java b/java/com/google/gerrit/git/RefUpdateUtil.java
index fa7b98f..bd88962 100644
--- a/java/com/google/gerrit/git/RefUpdateUtil.java
+++ b/java/com/google/gerrit/git/RefUpdateUtil.java
@@ -150,6 +150,7 @@
   public static void deleteChecked(Repository repo, String refName) throws IOException {
     RefUpdate ru = repo.updateRef(refName);
     ru.setForceUpdate(true);
+    ru.setCheckConflicting(false);
     switch (ru.delete()) {
       case FORCED:
         // Ref was deleted.
diff --git a/java/com/google/gerrit/pgm/init/InitModule.java b/java/com/google/gerrit/pgm/init/InitModule.java
index f2fc001..b658675 100644
--- a/java/com/google/gerrit/pgm/init/InitModule.java
+++ b/java/com/google/gerrit/pgm/init/InitModule.java
@@ -34,8 +34,6 @@
   @Override
   protected void configure() {
     bind(SitePaths.class);
-    bind(Libraries.class);
-    bind(LibraryDownloader.class);
     factory(Section.Factory.class);
     factory(VersionedAuthorizedKeysOnInit.Factory.class);
 
diff --git a/java/com/google/gerrit/pgm/init/Libraries.java b/java/com/google/gerrit/pgm/init/Libraries.java
deleted file mode 100644
index c599e99..0000000
--- a/java/com/google/gerrit/pgm/init/Libraries.java
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (C) 2009 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.pgm.init;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.gerrit.pgm.init.api.LibraryDownload;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.List;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-
-/** Standard {@link LibraryDownloader} instances derived from configuration. */
-@Singleton
-class Libraries {
-  private static final String RESOURCE_FILE = "com/google/gerrit/pgm/init/libraries.config";
-
-  private final Provider<LibraryDownloader> downloadProvider;
-  private final List<String> skippedDownloads;
-  private final boolean skipAllDownloads;
-
-  /* final */ LibraryDownloader db2Driver;
-  /* final */ LibraryDownloader db2DriverLicense;
-  /* final */ LibraryDownloader hanaDriver;
-  /* final */ LibraryDownloader mariadbDriver;
-  /* final */ LibraryDownloader mysqlDriver;
-  /* final */ LibraryDownloader oracleDriver;
-
-  @Inject
-  Libraries(
-      final Provider<LibraryDownloader> downloadProvider,
-      @LibraryDownload List<String> skippedDownloads,
-      @LibraryDownload Boolean skipAllDownloads) {
-    this.downloadProvider = downloadProvider;
-    this.skippedDownloads = skippedDownloads;
-    this.skipAllDownloads = skipAllDownloads;
-    init();
-  }
-
-  private void init() {
-    final Config cfg = new Config();
-    try {
-      cfg.fromText(read(RESOURCE_FILE));
-    } catch (IOException | ConfigInvalidException e) {
-      throw new RuntimeException(e.getMessage(), e);
-    }
-
-    for (Field f : Libraries.class.getDeclaredFields()) {
-      if ((f.getModifiers() & Modifier.STATIC) == 0 && f.getType() == LibraryDownloader.class) {
-        try {
-          f.set(this, downloadProvider.get());
-        } catch (IllegalArgumentException | IllegalAccessException e) {
-          throw new IllegalStateException("Cannot initialize " + f.getName());
-        }
-      }
-    }
-
-    for (Field f : Libraries.class.getDeclaredFields()) {
-      if ((f.getModifiers() & Modifier.STATIC) == 0 && f.getType() == LibraryDownloader.class) {
-        try {
-          init(f, cfg);
-        } catch (IllegalArgumentException
-            | IllegalAccessException
-            | NoSuchFieldException
-            | SecurityException e) {
-          throw new IllegalStateException("Cannot configure " + f.getName());
-        }
-      }
-    }
-  }
-
-  private void init(Field field, Config cfg)
-      throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
-          SecurityException {
-    String n = field.getName();
-    LibraryDownloader dl = (LibraryDownloader) field.get(this);
-    dl.setName(get(cfg, n, "name"));
-    dl.setJarUrl(get(cfg, n, "url"));
-    dl.setSHA1(getOptional(cfg, n, "sha1"));
-    dl.setRemove(get(cfg, n, "remove"));
-    for (String d : cfg.getStringList("library", n, "needs")) {
-      dl.addNeeds((LibraryDownloader) getClass().getDeclaredField(d).get(this));
-    }
-    dl.setSkipDownload(skipAllDownloads || skippedDownloads.contains(n));
-  }
-
-  private static String getOptional(Config cfg, String name, String key) {
-    return doGet(cfg, name, key, false);
-  }
-
-  private static String get(Config cfg, String name, String key) {
-    return doGet(cfg, name, key, true);
-  }
-
-  private static String doGet(Config cfg, String name, String key, boolean required) {
-    String val = cfg.getString("library", name, key);
-    if ((val == null || val.isEmpty()) && required) {
-      throw new IllegalStateException(
-          "Variable library." + name + "." + key + " is required within " + RESOURCE_FILE);
-    }
-    return val;
-  }
-
-  private static String read(String p) throws IOException {
-    try (InputStream in = Libraries.class.getClassLoader().getResourceAsStream(p)) {
-      if (in == null) {
-        throw new FileNotFoundException("Cannot load resource " + p);
-      }
-      try (Reader r = new InputStreamReader(in, UTF_8)) {
-        final StringBuilder buf = new StringBuilder();
-        final char[] tmp = new char[512];
-        int n;
-        while (0 < (n = r.read(tmp))) {
-          buf.append(tmp, 0, n);
-        }
-        return buf.toString();
-      }
-    }
-  }
-}
diff --git a/java/com/google/gerrit/pgm/init/LibraryDownloader.java b/java/com/google/gerrit/pgm/init/LibraryDownloader.java
deleted file mode 100644
index 0b31ee2..0000000
--- a/java/com/google/gerrit/pgm/init/LibraryDownloader.java
+++ /dev/null
@@ -1,316 +0,0 @@
-// Copyright (C) 2009 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.pgm.init;
-
-import com.google.common.hash.Funnels;
-import com.google.common.hash.Hasher;
-import com.google.common.hash.Hashing;
-import com.google.common.io.ByteStreams;
-import com.google.gerrit.common.Die;
-import com.google.gerrit.common.IoUtil;
-import com.google.gerrit.pgm.init.api.ConsoleUI;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.Proxy;
-import java.net.ProxySelector;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-import org.eclipse.jgit.util.HttpSupport;
-
-/** Get optional or required 3rd party library files into $site_path/lib. */
-class LibraryDownloader {
-  private final ConsoleUI ui;
-  private final Path lib_dir;
-  private final StaleLibraryRemover remover;
-
-  private boolean required;
-  private String name;
-  private String jarUrl;
-  private String sha1;
-  private String remove;
-  private List<LibraryDownloader> needs;
-  private LibraryDownloader neededBy;
-  private Path dst;
-  private boolean download; // download or copy
-  private boolean exists;
-  private boolean skipDownload;
-
-  @Inject
-  LibraryDownloader(ConsoleUI ui, SitePaths site, StaleLibraryRemover remover) {
-    this.ui = ui;
-    this.lib_dir = site.lib_dir;
-    this.remover = remover;
-    this.needs = new ArrayList<>(2);
-  }
-
-  void setName(String name) {
-    this.name = name;
-  }
-
-  void setJarUrl(String url) {
-    this.jarUrl = url;
-    download = jarUrl.startsWith("http");
-  }
-
-  void setSHA1(String sha1) {
-    this.sha1 = sha1;
-  }
-
-  void setRemove(String remove) {
-    this.remove = remove;
-  }
-
-  void addNeeds(LibraryDownloader lib) {
-    needs.add(lib);
-  }
-
-  void setSkipDownload(boolean skipDownload) {
-    this.skipDownload = skipDownload;
-  }
-
-  void downloadRequired() {
-    setRequired(true);
-    download();
-  }
-
-  void downloadOptional() {
-    required = false;
-    download();
-  }
-
-  private void setRequired(boolean r) {
-    required = r;
-    for (LibraryDownloader d : needs) {
-      d.setRequired(r);
-    }
-  }
-
-  private void download() {
-    if (skipDownload) {
-      return;
-    }
-
-    if (jarUrl == null || !jarUrl.contains("/")) {
-      throw new IllegalStateException("Invalid JarUrl for " + name);
-    }
-
-    final String jarName = jarUrl.substring(jarUrl.lastIndexOf('/') + 1);
-    if (jarName.contains("/") || jarName.contains("\\")) {
-      throw new IllegalStateException("Invalid JarUrl: " + jarUrl);
-    }
-
-    if (name == null) {
-      name = jarName;
-    }
-
-    dst = lib_dir.resolve(jarName);
-    if (Files.exists(dst)) {
-      exists = true;
-    } else if (shouldGet()) {
-      doGet();
-    }
-
-    if (exists) {
-      for (LibraryDownloader d : needs) {
-        d.neededBy = this;
-        d.downloadRequired();
-      }
-    }
-  }
-
-  private boolean shouldGet() {
-    if (ui.isBatch()) {
-      return required;
-    }
-    final StringBuilder msg = new StringBuilder();
-    msg.append("\n");
-    msg.append("Gerrit Code Review is not shipped with %s\n");
-    if (neededBy != null) {
-      msg.append(String.format("** This library is required by %s. **\n", neededBy.name));
-    } else if (required) {
-      msg.append("**  This library is required for your configuration. **\n");
-    } else {
-      msg.append("  If available, Gerrit can take advantage of features\n");
-      msg.append("  in the library, but will also function without it.\n");
-    }
-    msg.append(String.format("%s and install it now", download ? "Download" : "Copy"));
-    return ui.yesno(true, msg.toString(), name);
-  }
-
-  private void doGet() {
-    if (!Files.exists(lib_dir)) {
-      try {
-        Files.createDirectories(lib_dir);
-      } catch (IOException e) {
-        throw new Die("Cannot create " + lib_dir, e);
-      }
-    }
-
-    try {
-      remover.remove(remove);
-      if (download) {
-        doGetByHttp();
-      } else {
-        doGetByLocalCopy();
-      }
-      verifyFileChecksum();
-    } catch (IOException err) {
-      try {
-        Files.delete(dst);
-      } catch (IOException e) {
-        // Delete failed; leave alone.
-      }
-
-      if (ui.isBatch()) {
-        throw new Die("error: Cannot get " + jarUrl, err);
-      }
-
-      System.err.println();
-      System.err.println();
-      System.err.println("error: " + err.getMessage());
-      System.err.println("Please download:");
-      System.err.println();
-      System.err.println("  " + jarUrl);
-      System.err.println();
-      System.err.println("and save as:");
-      System.err.println();
-      System.err.println("  " + dst.toAbsolutePath());
-      System.err.println();
-      System.err.flush();
-
-      ui.waitForUser();
-
-      if (Files.exists(dst)) {
-        verifyFileChecksum();
-
-      } else if (!ui.yesno(!required, "Continue without this library")) {
-        throw new Die("aborted by user");
-      }
-    }
-
-    if (Files.exists(dst)) {
-      exists = true;
-      IoUtil.loadJARs(dst);
-    }
-  }
-
-  private void doGetByLocalCopy() throws IOException {
-    System.err.print("Copying " + jarUrl + " ...");
-    Path p = url2file(jarUrl);
-    if (!Files.exists(p)) {
-      StringBuilder msg =
-          new StringBuilder()
-              .append("\n")
-              .append("Can not find the %s at this location: %s\n")
-              .append("Please provide alternative URL");
-      p = url2file(ui.readString(null, msg.toString(), name, jarUrl));
-    }
-    Files.copy(p, dst);
-  }
-
-  private static Path url2file(String urlString) throws IOException {
-    final URL url = new URL(urlString);
-    try {
-      return Paths.get(url.toURI());
-    } catch (URISyntaxException e) {
-      return Paths.get(url.getPath());
-    }
-  }
-
-  private void doGetByHttp() throws IOException {
-    System.err.print("Downloading " + jarUrl + " ...");
-    System.err.flush();
-    try (InputStream in = openHttpStream(jarUrl);
-        OutputStream out = Files.newOutputStream(dst)) {
-      ByteStreams.copy(in, out);
-      System.err.println(" OK");
-      System.err.flush();
-    } catch (IOException err) {
-      deleteDst();
-      System.err.println(" !! FAIL !!");
-      System.err.println(err);
-      System.err.flush();
-      throw err;
-    }
-  }
-
-  private static InputStream openHttpStream(String urlStr) throws IOException {
-    ProxySelector proxySelector = ProxySelector.getDefault();
-    URL url = new URL(urlStr);
-    Proxy proxy = HttpSupport.proxyFor(proxySelector, url);
-    HttpURLConnection c = (HttpURLConnection) url.openConnection(proxy);
-
-    switch (HttpSupport.response(c)) {
-      case HttpURLConnection.HTTP_OK:
-        return c.getInputStream();
-
-      case HttpURLConnection.HTTP_NOT_FOUND:
-        throw new FileNotFoundException(url.toString());
-
-      default:
-        throw new IOException(
-            url.toString() + ": " + HttpSupport.response(c) + " " + c.getResponseMessage());
-    }
-  }
-
-  @SuppressWarnings("deprecation") // Use Hashing.sha1 for compatibility.
-  private void verifyFileChecksum() {
-    if (sha1 == null) {
-      System.err.println();
-      System.err.flush();
-      return;
-    }
-    Hasher h = Hashing.sha1().newHasher();
-    try (InputStream in = Files.newInputStream(dst);
-        OutputStream out = Funnels.asOutputStream(h)) {
-      ByteStreams.copy(in, out);
-    } catch (IOException e) {
-      deleteDst();
-      throw new Die("cannot checksum " + dst, e);
-    }
-    if (sha1.equals(h.hash().toString())) {
-      System.err.println("Checksum " + dst.getFileName() + " OK");
-      System.err.flush();
-    } else if (ui.isBatch()) {
-      deleteDst();
-      throw new Die(dst + " SHA-1 checksum does not match");
-
-    } else if (!ui.yesno(
-        null /* force an answer */,
-        "error: SHA-1 checksum does not match\nUse %s anyway", //
-        dst.getFileName())) {
-      deleteDst();
-      throw new Die("aborted by user");
-    }
-  }
-
-  private void deleteDst() {
-    try {
-      Files.delete(dst);
-    } catch (IOException e) {
-      System.err.println(" Failed to clean up lib: " + dst);
-    }
-  }
-}
diff --git a/javatests/com/google/gerrit/pgm/init/LibrariesTest.java b/javatests/com/google/gerrit/pgm/init/LibrariesTest.java
deleted file mode 100644
index 5aa4718..0000000
--- a/javatests/com/google/gerrit/pgm/init/LibrariesTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2009 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.pgm.init;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-import com.google.gerrit.pgm.init.api.ConsoleUI;
-import com.google.gerrit.server.config.SitePaths;
-import java.nio.file.Paths;
-import java.util.Collections;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public class LibrariesTest {
-  @Mock ConsoleUI ui;
-  @Mock StaleLibraryRemover remover;
-
-  @Test
-  public void create() throws Exception {
-    final SitePaths site = new SitePaths(Paths.get("."));
-
-    Libraries lib =
-        new Libraries(
-            () -> new LibraryDownloader(ui, site, remover), Collections.emptyList(), false);
-
-    assertNotNull(lib.mysqlDriver);
-    verifyZeroInteractions(ui);
-    verifyZeroInteractions(remover);
-  }
-}
diff --git a/resources/com/google/gerrit/pgm/init/libraries.config b/resources/com/google/gerrit/pgm/init/libraries.config
deleted file mode 100644
index 3d3545b..0000000
--- a/resources/com/google/gerrit/pgm/init/libraries.config
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2009 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.
-
-[library "mysqlDriver"]
-  name = MySQL Connector/J 5.1.43
-  url = https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.43/mysql-connector-java-5.1.43.jar
-  sha1 = dee9103eec0d877f3a21c82d4d9e9f4fbd2d6e0a
-  remove = mysql-connector-java-.*[.]jar
-
-[library "mariadbDriver"]
-  name = MariaDB Connector/J 2.3.0
-  url = https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/2.3.0/mariadb-java-client-2.3.0.jar
-  sha1 = c2b1a6002a169757d0649449288e9b3b776af76b
-  remove = mariadb-java-client-.*[.]jar
-
-[library "oracleDriver"]
-  name = Oracle JDBC driver 11g Release 2 (11.2.0)
-  url = file:///u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar
-  sha1 = 2f89cd9176772c3a6c261ce6a8e3d0d4425f5679
-  remove = ojdbc6.jar
-
-[library "db2Driver"]
-  name = DB2 Type 4 JDBC driver (10.5)
-  url = file:///opt/ibm/db2/V10.5/java/db2jcc4.jar
-  sha1 = 9344d4fd41d6511f2d1d1deb7759056495b3a39b
-  needs = db2DriverLicense
-  remove = db2jcc4.jar
-
-# Omit SHA-1 for license JAR as it's not stable and depends on the product
-# the customer has purchased.
-[library "db2DriverLicense"]
-  name = DB2 Type 4 JDBC driver license (10.5)
-  url = file:///opt/ibm/db2/V10.5/java/db2jcc_license_cu.jar
-  remove = db2jcc_license_cu.jar
-
-[library "hanaDriver"]
-  name = HANA JDBC driver
-  url = file:///usr/sap/hdbclient/ngdbc.jar
-  remove = ngdbc.jar