Added pluginsInfo capability.

Adding 3 elements in the produced zip file, specifying plugins content,
lib content and the versions for each plugin.

Change-Id: Ib1c8bc4830114f74791145241a3af64979a013cd
diff --git a/src/main/resources/Documentation/rest-api.md b/src/main/resources/Documentation/rest-api.md
index 2f7d684..1595492 100644
--- a/src/main/resources/Documentation/rest-api.md
+++ b/src/main/resources/Documentation/rest-api.md
@@ -31,7 +31,42 @@
 }
 ```
 - configInfo - This will add in the zip all the *.config files in the $GERRIT/etc folder
+- pluginsInfo - This will add 3 elements in the zip containing the plugins_dir,
+  lib_dir and the versions for each plugin. Here a possible output:
+```
+lib_dir:
+[
+  {
+    "name": "github-oauth.jar",
+    "perms": "rw-rw-r--",
+    "owner": "gerrit",
+    "group": "gerrit",
+    "date": "2017-06-04T08:35:02Z",
+    "size": 128567
+  }
+]
 
+plugins_dir:
+[
+  {
+    "name": "gerrit-support.jar",
+    "perms": "rw-rw-r--",
+    "owner": "gerrit",
+    "group": "gerrit",
+    "date": "2017-06-04T08:35:02Z",
+    "size": 9639813
+  }
+]
+
+plugins_versions:
+{
+     "gerrit-support": {
+        "id"       : "gerrit-support",
+        "version"  : "1.0",
+        "indexUrl" : "/plugins/myplugin",
+        "disabled" : false }
+}
+```
 NOTE: API must be authenticated with the credentials of a user with the
 'Collect Server Data' capability.
 
diff --git a/src/main/scala/com/googlesource/gerrit/plugins/support/GerritFacade.scala b/src/main/scala/com/googlesource/gerrit/plugins/support/GerritFacade.scala
index 2736b9f..1bb9b0c 100644
--- a/src/main/scala/com/googlesource/gerrit/plugins/support/GerritFacade.scala
+++ b/src/main/scala/com/googlesource/gerrit/plugins/support/GerritFacade.scala
@@ -16,7 +16,11 @@
 
 package com.googlesource.gerrit.plugins.support
 
+import com.google.gerrit.extensions.restapi.TopLevelResource
 import com.google.gerrit.server.account.CapabilityControl
+import com.google.gerrit.server.plugins.{ListPlugins, PluginsCollection}
+import com.google.gson.JsonElement
+import com.google.inject.{ImplementedBy, Inject}
 import com.googlesource.gerrit.plugins.support.latest.LatestCapabilityControl
 import com.googlesource.gerrit.plugins.support.legacy.LegacyCapabilityControl
 
@@ -45,3 +49,22 @@
     }
   }
 }
+
+// Structure of the JSON fields returned by the pluginsInfo method
+case class PluginInfo(id: String, version: String, indexUrl: String, disabled: Boolean)
+
+@ImplementedBy(classOf[GerritPluginsInfoProvider])
+trait PluginsInfoProvider {
+
+  def getPluginsInfo: JsonElement
+}
+
+class GerritPluginsInfoProvider @Inject() (val pluginsCollection: PluginsCollection) extends PluginsInfoProvider {
+
+  override def getPluginsInfo: JsonElement = {
+    pluginsCollection
+      .list
+      .asInstanceOf[ListPlugins]
+      .apply(TopLevelResource.INSTANCE).asInstanceOf[JsonElement]
+  }
+}
diff --git a/src/main/scala/com/googlesource/gerrit/plugins/support/commands/FileAttribute.scala b/src/main/scala/com/googlesource/gerrit/plugins/support/commands/FileAttribute.scala
new file mode 100644
index 0000000..e5a5b27
--- /dev/null
+++ b/src/main/scala/com/googlesource/gerrit/plugins/support/commands/FileAttribute.scala
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.support.commands
+
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.attribute.{BasicFileAttributes, PosixFileAttributeView, PosixFileAttributes, PosixFilePermissions}
+
+case class FileAttribute(name: String, perms: String,
+                         owner: String, group: String,
+                         date: String, size: Long)
+
+object FileAttribute {
+  def from(f: File) = {
+    val p = f.toPath
+
+    FileAttribute(
+      name = f.getName,
+      perms = PosixFilePermissions.toString(
+        Files.getFileAttributeView(p, classOf[PosixFileAttributeView])
+          .readAttributes.permissions),
+      owner = Files.getOwner(p).getName,
+      group = Files.readAttributes(p, classOf[PosixFileAttributes])
+        .group.getName,
+      date =
+        Files.readAttributes(p, classOf[BasicFileAttributes])
+          .creationTime().toString,
+      size = f.length
+    )
+  }
+}
diff --git a/src/main/scala/com/googlesource/gerrit/plugins/support/commands/PluginsInfoCommand.scala b/src/main/scala/com/googlesource/gerrit/plugins/support/commands/PluginsInfoCommand.scala
new file mode 100644
index 0000000..b2837d3
--- /dev/null
+++ b/src/main/scala/com/googlesource/gerrit/plugins/support/commands/PluginsInfoCommand.scala
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.support.commands
+
+import java.io.StringWriter
+
+import com.google.gson.JsonArray
+import com.google.inject.Inject
+import com.googlesource.gerrit.plugins.support.GerritSupportCommand.{CommandResult, JsonResult}
+import com.googlesource.gerrit.plugins.support.{GerritSupportCommand, PluginsInfoProvider, SitePathsWrapper}
+
+class PluginsInfoCommand @Inject()(val sitePathsWrapper: SitePathsWrapper,
+                                   val pluginsInfoProvider: PluginsInfoProvider)
+  extends GerritSupportCommand {
+
+  override def getResults = {
+    try {
+      Seq(
+        listDir("plugins_dir"),
+        listDir("lib_dir"),
+        listPluginsVersions
+      )
+    } catch {
+      case e: Exception => {
+        val error = s"Error while processing pluginsInfo ${e
+          .getMessage}"
+        log.error(error,e)
+        Seq(CommandResult(name, error))
+      }
+    }
+  }
+
+  private def listDir(dir: String): CommandResult = {
+    val jsonArray = new JsonArray()
+    sitePathsWrapper
+      .getAsPath(dir)
+      .toFile
+      .listFiles
+      .map(FileAttribute.from)
+      .map(gson.toJsonTree)
+      .foreach(jsonArray.add)
+
+    CommandResult(dir, JsonResult(jsonArray))
+  }
+
+  private def listPluginsVersions = {
+    val outString = new StringWriter
+
+    CommandResult("plugins_versions",
+      JsonResult(gson.toJsonTree(pluginsInfoProvider.getPluginsInfo))
+    )
+  }
+}
diff --git a/src/test/scala/com/googlesource/gerrit/plugins/support/GerritSupportTest.scala b/src/test/scala/com/googlesource/gerrit/plugins/support/GerritSupportTest.scala
index 9384c37..833946d 100644
--- a/src/test/scala/com/googlesource/gerrit/plugins/support/GerritSupportTest.scala
+++ b/src/test/scala/com/googlesource/gerrit/plugins/support/GerritSupportTest.scala
@@ -18,6 +18,7 @@
 
 import java.nio.file.Paths
 
+import com.google.gson.{Gson, JsonElement}
 import com.googlesource.gerrit.plugins.support.GerritSupportCommand._
 import com.googlesource.gerrit.plugins.support.commands._
 import org.mockito.Matchers.any
@@ -72,5 +73,39 @@
     content should not be empty
   }
 
+  "plugins-info command" should "return some data" in {
+    val gson = new Gson
+
+    // returns a valid tmp path for any path requested
+    val mockedWrapper = {
+      val mock = Mockito.mock(classOf[SitePathsWrapper])
+      Mockito.when(mock.getAsPath(any[String])).thenReturn(tmpPath.toPath)
+      mock
+    }
+
+    val pluginsInfo = gson.toJsonTree(
+      Map("myplugin" -> PluginInfo("pluginId", "1.0", "/plugins/myplugin", false)))
+    val mockedPluginsCollection = new PluginsInfoProvider { def getPluginsInfo = pluginsInfo }
+
+    // assert we returns three entries for plugins_dir, lib_dir and
+    // plugins_version
+    val Seq(plugins_dir, lib_dir, plugins_versions) =
+    new PluginsInfoCommand(
+      mockedWrapper,
+      mockedPluginsCollection).execute
+
+    plugins_dir.entryName.value should be("plugins_dir")
+    val JsonResult(plugins_dir_result) = plugins_dir.content
+    plugins_dir_result.getAsJsonArray.size should not be 0
+
+    lib_dir.entryName.value should be("lib_dir")
+    val JsonResult(lib_dir_result) = lib_dir.content
+    lib_dir_result.getAsJsonArray.size should not be 0
+
+    plugins_versions.entryName.value should be("plugins_versions")
+    val JsonResult(pluginsInfoJson) = plugins_versions.content
+
+    pluginsInfoJson should be (pluginsInfo)
+  }
 }