Add IP Log generation scripts.

Given a composite metadata repository and a path against which to
resolve the child repositories (that have embedded iplog metadata), this
script will generate an HTML webpage displaying all the relevant
information.
diff --git a/scripts/iplog.php b/scripts/iplog.php
new file mode 100644
index 0000000..ed84397
--- /dev/null
+++ b/scripts/iplog.php
@@ -0,0 +1,349 @@
+<?php
+
+//ini_set("display_errors", "true");
+//error_reporting (E_ALL);
+$serverName = $_SERVER["SERVER_NAME"];
+if ("download.eclipse.org" === $serverName) {
+  include "dlconfigOnDownloads.php";
+} else {
+  include "dlconfig.php";
+}
+$ipouterrors= array();
+
+include $relativePath."/commonFiles/orbitUtilities.php";
+// previous page is for "bread crumbs"
+
+# get path to use when resolving relative children repositories
+if (isset($_GET['repoPath'])) {
+	$rPath = $_GET['repoPath'];
+	$repoPath = 'http://download.eclipse.org/' . $rPath;
+	$buildlabel = substr($rPath, strrpos($rPath, '/') + 1);
+} else {
+	echo 'Missing repoPath.';
+	exit;
+}
+
+$pageTitle="Orbit Build $buildlabel";
+
+// curPageURL returns URL segments
+// http://build.eclipse.org/orbit/projects/                                 ==> http://build.eclipse.org/orbit/projects/
+// http://build.eclipse.org/orbit/projects/index.php                        ==> http://build.eclipse.org/orbit/projects/
+// http://build.eclipse.org/orbit/projects/index.php?value=name             ==> http://build.eclipse.org/orbit/projects/
+// echo "current base URL: " . curPageURL();
+
+//$ipoutfilename=__DIR__."/commonFiles/iplogFileErrors.txt";
+//chmod($ipoutfilename, 0666);
+//file_put_contents ($ipoutfilename,"");
+
+
+function curPageURL() {
+
+    $isHTTPS = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on");
+    $port = (isset($_SERVER["SERVER_PORT"]) && ((!$isHTTPS && $_SERVER["SERVER_PORT"] != "80") || ($isHTTPS && $_SERVER["SERVER_PORT"] != "443")));
+    $port = ($port) ? ':'.$_SERVER["SERVER_PORT"] : '';
+    $url = ($isHTTPS ? 'https://' : 'http://').$_SERVER["SERVER_NAME"].$port.$_SERVER["REQUEST_URI"];
+
+
+    $pattern = "!(^.*/)" . "index\.php(\?.*)*$!";
+
+    $matched = preg_match($pattern, $url, $matches);
+    if ($matched)
+    {
+        $url=$matches[1];
+    }
+    return $url;
+
+}
+
+// test of curPageURL
+// http://build.eclipse.org/orbit/projects/                                 ==> http://build.eclipse.org/orbit/projects/
+// http://build.eclipse.org/orbit/projects/index.php                        ==> http://build.eclipse.org/orbit/projects/
+// http://build.eclipse.org/orbit/projects/index.php?value=name             ==> http://build.eclipse.org/orbit/projects/
+// echo "current base URL: " . curPageURL();
+
+
+require $relativePath."/commonFiles/DL.header.php.html";
+include "$relativePath"."/commonFiles/tinyReminder.php";
+
+?>
+
+<?php
+
+// detect if on Eclipse download machine and use Eclipse mirror URLs
+$pos = strpos(getcwd(),'/tools/orbit/committers/drops/');
+if($pos === false) {
+  $downloadUrlPrefix = '';
+  $downloadWithRedirectUrlPrefix = '';
+}
+else {
+  $downloadUrlPrefix = 'http://www.eclipse.org/downloads/download.php?file=/tools/orbit/committers/drops/' . $buildlabel . '/';
+  $downloadWithRedirectUrlPrefix = 'http://www.eclipse.org/downloads/download.php?r=1&file=/tools/orbit/committers/drops/' . $buildlabel . '/';
+}
+
+# get repo content
+if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
+        if ( $_SERVER['CONTENT_LENGTH'] > 1000) {
+          echo 'Input repository is too large. The input repository should ' .
+               'be a composite metadata repository.';
+          exit;
+
+        }
+        $repoXml = trim(file_get_contents('php://input'));
+}
+
+# read composite content
+$xmlDoc = new DOMDocument();
+if ( isset($repoXml) ) {
+        $xmlDoc->loadXML( $repoXml );
+} else {
+        if(!headers_sent())
+                header($_SERVER['SERVER_PROTOCOL'].' 400 Missing Repo Data', true, 400);
+        else
+                die('Need repo data!');
+        exit;
+}
+
+$xslContent = file_get_contents( 'repo-index.xsl' );
+$xslDoc = new DOMDocument();
+$xslDoc->loadXML( $xslContent );
+
+# transform to HTML
+$proc = new XSLTProcessor();
+$proc->importStylesheet( $xslDoc );
+
+echo "<h1>Orbit Build: $buildlabel</h1>";
+echo "<p><b>This page is a work in progress.</b></p>";
+echo "<p>The repository offered by this build is a composite of the content seen here and the last 'old' R (Recommended) build. The metadata from that build is not yet fully displayed below so to view it please go to <a href=\"http://download.eclipse.org/tools/orbit/downloads/drops/R20160520211859/\">R20160520211859</a>.<p>";
+
+echo "<h2>Useful Information</h2>";
+echo "<p>In addition to the bundles themselves, the following maps, project sets, and test results are useful for committers and consumers:</p>";
+
+// If a build fails, especially early, many of the following files won't exist.
+// So, we don't produce "link" to them. But, we still leave a descriptive line, in case they
+// disappear (are not produced) for some other reason, then we'd notice.
+// Subsequently decided to put in "marker" file for when build "fails early", so
+// for now will skip these files we know won't exist, if we fail early, but
+// basic logic (of checking for existence) is still sound.
+// Note: the above "directory.txt" file should always exist, as it is
+// "raw" map file, retrieved first, and used during the build, so might be
+// required to help debugging a failure.
+
+$failedEarlyfile="buildFailedEarly.txt";
+if (!file_exists($failedEarlyfile)) {
+
+       $fname="iplog-$buildlabel.html";
+       if (file_exists($fname)) {
+         echo "<a href=\"$fname\">Bundle IP Log Information</a><br />";
+       } else {
+         echo "Bundle IP Log Information does not exist. <br />";
+       }
+
+       // Caution this "antBuilderOutput" is here twice, once, here, for
+       // normal case, and later in "failed early" else case.
+       // TODO: there's gotta be a better way.
+       $fname="antBuilderOutput.log";
+       if (file_exists($fname)) {
+          echo "<a href=\"$fname\">Ant Build Output</a><br />";
+       } else {
+          echo "Ant Build Output does not exist. <br />";
+       }
+
+
+       $fname="comparator.log";
+       if (file_exists($fname)) {
+         echo "<a href=\"$fname\">P2 Mirror Comparator Output</a><br />";
+       } else {
+          echo "P2 Mirror Comparator Output does not exist. <br />";
+       }
+
+       // the referenceRepoExists (and Not) file is an indicator file whose contents is the name of the
+       // reference repo we tried to use in the build. Elsewhere, we test for mere existence of the repository,
+       // and use "fail" icon if it did not exist,
+       // but even if it exists, that does not necessarily mean it is the right repository to be using, so we display
+       // the name of the repository directory for easy manual inspection (though, is a little redundent with
+       // comparator log).
+       // note to self: use file_get_contents to reading contents into a string.
+       if (file_exists("referenceRepoExists")) {
+           echo "<img class=\"cs\" alt=\"Repo exists\" src=\"".$relativePath."/commonFiles/Checkmark.gif\" /> Repo used for comparison during mirroring: ";
+           readfile("referenceRepoExists");
+               echo "<br />";
+       }
+       elseif (file_exists("referenceRepoExistsNot")) {
+         echo "<img class=\"cs\" alt=\"Repo not found\" src=\"".$relativePath."/commonFiles/Fail.gif\" /> Bad build. Repo to use while mirroring was not found:<br />\n";
+               readfile("referenceRepoExistsNot");
+               echo "<br />\n";
+       }
+       else {
+              echo "<img class=\"cs\" alt=\"Repo indicator file not found\" src=\"".$relativePath."/commonFiles/Fail.gif\" /><br />\n";
+               echo "Repository indicator file not found. Probably a bad build or build script error? Check Ant Build Output.";
+               echo "<br />\n";
+        }
+
+       $fname="results-$buildlabel.xml";
+       if (file_exists($fname)) {
+              echo "<a href=\"results-$buildlabel.xml\">\n";
+              // the testsFailed.txt file is an intentional indicator (only) file
+              if (file_exists("testsFailed.txt")) {
+                  echo "<img class=\"cs\" alt=\"Tests Failed\" src=\"".$relativePath."/commonFiles/Fail.gif\" />\n";
+              }
+              elseif (file_exists("testsOk.txt")) {
+                  echo "<img class=\"cs\" alt=\"Tests Passed\" src=\"".$relativePath."/commonFiles/Checkmark.gif\" />\n";
+              }
+              echo "Test Results</a><br />\n";
+       } else {
+              echo "Test Results do not exist. <br />\n";
+       }
+
+    // We get the list "early" so we have results of IP Log checks at right point in page.
+    // $bundleList = getBundleList("./repository/plugins");
+
+    $sizeofiperrors=count($ipouterrors);
+    //echo "count: $sizeofiperrors <br />";
+    //echo "contents: <br />";
+    //dump($ipouterrors);
+    if ($sizeofiperrors > 0) {
+        // this likely won't have "permission" to write files on web server.
+        // we execute the index.php file during "build" to get this file written.
+        $filenameerr="testsFailedIPLog.txt";
+        $dataerr="";
+        foreach ($ipouterrors as $iperrs) {
+           $dataerr = $dataerr.$iperrs."<br />\n";
+        }
+        file_put_contents ($filenameerr, $dataerr);
+        //
+        echo "<img class=\"cs\" alt=\"Checks Failed\" src=\"".$relativePath."/commonFiles/Fail.gif\" />" . "IP Log XML File Checks: Errors Found: <br />\n";
+        foreach ($ipouterrors as $iperrs) {
+           echo "<li>".$iperrs."</li>\n";
+        }
+    } else {
+        echo "<img class=\"cs\" alt=\"Checks Ok\" src=\"".$relativePath."/commonFiles/Checkmark.gif\" />" ."IP Log XML File Checks: Ok <br />\n";
+    }
+
+    echo "</p>\n";
+
+       $currentPageURLSegments = curPageURL();
+       echo "<h2>Orbit Build Repository</h2>\n";
+       echo "<p>For HTTP access, a p2 repository for this specific build can be found by adding 'repository' to the end of this download site URL, namely:<br />";
+       echo "<a href=\"${currentPageURLSegments}repository/\">${currentPageURLSegments}repository/</a></p>";
+
+if (file_exists("repository/index.xml.gz")) {
+       echo "<p>For use with tools such as <a href=\"http://bndtools.org/\">bndtools</a>, an <a href=\"http://www.osgi.org/Main/HomePage\">OSGi</a> repository index file for this specific build can be found by adding 'repository/index.xml.gz' to the end of this download site URL, namely:<br />";
+       echo "<a href=\"${currentPageURLSegments}repository/index.xml.gz\">${currentPageURLSegments}repository/index.xml.gz</a></p>";
+}
+
+       $displayablezipfilesize=fileSizeForDisplay("orbit-buildrepo-$buildlabel.zip");
+
+       echo "<h2>Zipped Orbit Build Repository</h2>";
+       echo "<p>The following zip file is a compressed-archive version of the above repository, for those that need or desire to have a copy of the whole repository on their local machine:<br />";
+       echo "<a href=\"{$downloadUrlPrefix}orbit-buildrepo-$buildlabel.zip\">orbit-buildrepo-$buildlabel.zip</a> (<a href=\"checksum/orbit-buildrepo-$buildlabel.zip.md5\">md5</a>) (<a href=\"checksum/orbit-buildrepo-$buildlabel.zip.sha1\">sha1</a>) $displayablezipfilesize</p>";
+
+} else {
+       echo "Build failed early. Check <a href=\"$failedEarlyfile\">short summary</a> or full Ant Build Output.<br />";
+       // Caution this "antBuilderOutput" is here twice, once, here, for
+       // "failed early" case, and above, for normal case. TODO: there's gotta be a better way.
+       $fname="antBuilderOutput.log";
+       if (file_exists($fname)) {
+          echo "<a href=\"$fname\">Ant Build Output</a><br />";
+       } else {
+          echo "Ant Build Output does not exist. <br />";
+       }
+}
+
+
+$childRepoXmlList = array();
+$children = $xmlDoc->getElementsByTagName('child');
+
+foreach ($children as $c) {
+  $childLoc = $c->getAttribute('location');
+  $proc->setParameter(null, 'repoPath', $repoPath . '/' . $childLoc);
+
+  $childContent = file_get_contents($repoPath . '/' . $childLoc . '/' . 'content.jar');
+  if (! $childContent) {
+    echo 'Could not find ' . $childLoc . '/' . 'content.jar' . ' on server.';
+    exit;
+  }
+  $childRepoFile = tmpfile();
+  $ret = fwrite($childRepoFile, $childContent);
+  if (! $ret) {
+    echo 'Failed to read ' . $childLoc . '/' . 'content.jar';
+    exit;
+  }
+  $childRepoFileName = stream_get_meta_data($childRepoFile)['uri'];
+  $zip = zip_open($childRepoFileName);
+  while ($zip_entry = zip_read($zip)) {
+    if (zip_entry_name($zip_entry) == 'content.xml' &&
+        zip_entry_open($zip, $zip_entry, "r")) {
+      $fstream = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
+      zip_entry_close($zip_entry);
+    }
+  }
+  zip_close($zip);
+  fclose($childRepoFile);
+
+  $childRepoXml = trim($fstream);
+  array_push($childRepoXmlList, $childRepoXml);
+}
+
+
+$nBundles = 0;
+$nDistinctBundles = 0;
+$nTotalBundles =  0;
+$nDistinctCQs = 0;
+$listOfCQs = "";
+
+foreach ($childRepoXmlList as $childRepoXml) {
+  $childXmlDoc = new DOMDocument();
+  $childXmlDoc->loadXML($childRepoXml);
+  $xpath = new DOMXpath($childXmlDoc);
+
+  $nBundles += $xpath->evaluate("count(/repository/units/unit[provides/provided/@namespace='org.eclipse.equinox.p2.eclipse.type' and provides/provided/@name='bundle']/@id)");
+  $nDistinctBundles += $xpath->evaluate("count(/repository/units/unit[provides/provided/@namespace='org.eclipse.equinox.p2.eclipse.type' and provides/provided/@name='bundle' and not(preceding::unit/@id=../../@id)]/@id)");
+  $nTotalBundles += $xpath->evaluate("count(/repository/units/unit[provides/provided/@name='bundle' or provides/provided/@name='source']/@id)");
+  $nDistinctCQs += $xpath->evaluate("count(/repository/units/unit/properties/property[@name='iplog.bug_id' and not(preceding::property/@value=@value)])");
+  $cqNodeList = $xpath->evaluate("/repository/units/unit/properties/property[@name='iplog.bug_id' and not(preceding::property/@value=@value)]/@value");
+  foreach ($cqNodeList as $node) {
+    $listOfCQs .= "," . $node->nodeValue;
+  }
+}
+
+$ipzillaURL = "https://dev.eclipse.org/ipzilla/buglist.cgi?bug_id=" . $listOfCQs;
+
+echo "<h2>Individual Bundles</h2>";
+
+echo "<h3>Statistics</h3>";
+echo "<p>Number of distinct third party packages: $nDistinctBundles.<br />";
+echo "Number of bundles (including different versions): $nBundles. <br />";
+echo "Total number of bundles (including source): $nTotalBundles.";
+
+echo "<h3>CQ Bookkeeping</h3>";
+echo "Total number of distinct CQs: $nDistinctCQs. <br />";
+echo "IPZilla <a href=\"$ipzillaURL\">list of all CQs</a></p>";
+echo "<p>Note: IPZilla is a database for tracking CQs (Contribution Questionnaires) and is only accessible by committers, ";
+echo "since in theory they might contain some non-public information. But, don't worry ... if you are not a committer you are not";
+echo " missing anything ... they are pretty dry! These (and the links provided here) are just some aides for committers to double check our book-keeping.</p>";
+
+echo "<h3>Table of Bundles</h3>";
+echo "<table class=\"table table-striped table-condensed\">";
+echo "<tr>";
+echo "<th align=\"left\">Bundle</th>";
+echo "<th>Source</th>";
+echo "<th>Version</th>";
+echo "<th>Orbit CQ</th>";
+echo "<th>Orbit Contact</th>";
+echo "</tr>";
+
+foreach ($childRepoXmlList as $childRepoXml) {
+  $childXmlDoc = new DOMDocument();
+  $childXmlDoc->loadXML($childRepoXml);
+  $html = $proc->transformToXML( $childXmlDoc );
+  echo "$html";
+}
+
+echo "</table>";
+echo "<p>Note: entries marked with 'unzip' are intented to be unzipped in a normal IDE environment, to work as intended (even though the file to download is a jar file).</p>";
+
+include $relativePath."/commonFiles/footerSUA.html";
+include $relativePath."/commonFiles/footerWebmaster.html";
+require $relativePath."/commonFiles/DL.footer.php.html";
+
+?>
diff --git a/scripts/repo-index.xsl b/scripts/repo-index.xsl
new file mode 100644
index 0000000..23ca615
--- /dev/null
+++ b/scripts/repo-index.xsl
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl" version="1.0">
+
+  <xsl:output omit-xml-declaration="yes" indent="yes"/>
+
+  <xsl:param name="repoPath"/>
+
+  <xsl:template match="/">
+
+      <xsl:for-each select="/repository/units/unit[provides/provided/@namespace = 'org.eclipse.equinox.p2.eclipse.type' and provides/provided/@name = 'bundle']">
+      <xsl:sort select="@id"/>
+      <xsl:if test="properties/property[@name='iplog.bug_id']">
+      <xsl:variable name="email" select="properties/property[@name='iplog.contact.email']/@value"/>
+
+<tr valign="top">
+  <td width="20%" >
+    <a href="{$repoPath}/plugins/{@id}_{@version}.jar">
+    <xsl:value-of select="@id" />
+    </a>
+  </td>
+  <td width="5%">
+    <a href="{$repoPath}/plugins/{@id}.source_{@version}.jar">
+    (source)
+    </a>
+  </td>
+  <td width="5%">
+    <!-- translate() replaces individual characters -->
+    <xsl:value-of select="translate(properties/property[@name='maven-version']/@value,'-SNAPSHOT','')" />
+  </td>
+  <td width="8%">
+    <a href="https://dev.eclipse.org/ipzilla/show_bug.cgi?id={properties/property[@name='iplog.bug_id']/@value}">
+    <xsl:value-of select="properties/property[@name='iplog.bug_id']/@value" />
+    </a>
+  </td>
+  <td width="15%">
+    <a href="mailto:{$email}">
+    <xsl:value-of select="properties/property[@name='iplog.contact.name']/@value" />
+    </a>
+  </td>
+</tr>
+      </xsl:if>
+      </xsl:for-each>
+
+  </xsl:template>
+
+</xsl:stylesheet>