Browse Source

updated spdx-license handling to include metadata

issue https://github.com/composer/composer/issues/3744

summary of changes
- replaced bin script "fetch-spdx-identifiers" by "update-spdx-licenses"
- "update-spdx-licenses" uses "Composer/Util/SpdxLicensesUpdater" to update "spdx-licenses.json" file with license identifier, fullname and osi-approved-status
- dropped "spdx-identifiers.json" (identifiers only)
- dropped "SpdxLicenseIdentifier", it's just "SpdxLicense" now
- modified "ShowCommand" to output the license with metadata and removed some unused method arguments (cleanup)
jakoch 10 years ago
parent
commit
74a077dd3d

+ 0 - 85
bin/fetch-spdx-identifiers

@@ -1,85 +0,0 @@
-#!/usr/bin/env php
-<?php
-
-$identifiers = new SPDXLicenseIdentifiersOnline;
-$printer = new JsonPrinter;
-$printer->printStringArray($identifiers->getStrings());
-
-/**
- * SPDX Identifier List from the registry.
- */
-class SPDXLicenseIdentifiersOnline
-{
-    const REGISTRY = 'http://www.spdx.org/licenses/';
-    const EXPRESSION = '//*[@typeof="spdx:License"]/code[@property="spdx:licenseId"]/text()';
-
-    private $identifiers;
-
-    /**
-     * @return array
-     */
-    public function getStrings()
-    {
-        if ($this->identifiers) {
-            return $this->identifiers;
-        }
-        $this->identifiers = $this->importNodesFromURL(
-            self::REGISTRY,
-            self::EXPRESSION
-        );
-
-        return $this->identifiers;
-    }
-
-    private function importNodesFromURL($url, $expressionTextNodes)
-    {
-        $doc = new DOMDocument();
-        $doc->loadHTMLFile($url);
-        $xp = new DOMXPath($doc);
-        $codes = $xp->query($expressionTextNodes);
-        if (!$codes) {
-            throw new \Exception(sprintf('XPath query failed: %s', $expressionTextNodes));
-        }
-        if ($codes->length < 20) {
-            throw new \Exception('Obtaining the license table failed, there can not be less than 20 identifiers.');
-        }
-        $identifiers = array();
-        foreach ($codes as $code) {
-            $identifiers[] = $code->nodeValue;
-        }
-
-        return $identifiers;
-    }
-}
-
-/**
- * Print an array the way this script needs it.
- */
-class JsonPrinter
-{
-    /**
-     *
-     * @param array $array
-     */
-    public function printStringArray(array $array)
-    {
-        $lines = array('');
-        $line = &$lines[0];
-        $last = count($array) - 1;
-        foreach ($array as $item => $code) {
-            $code = sprintf('"%s"%s', trim($code), $item === $last ? '' : ', ');
-            $length = strlen($line) + strlen($code) - 1;
-            if ($length > 76) {
-                $line = rtrim($line);
-                unset($line);
-                $lines[] = $code;
-                $line = &$lines[count($lines) - 1];
-            } else {
-                $line .= $code;
-            }
-        }
-        $json = sprintf("[%s]", implode("\n    ", $lines));
-        $json = str_replace(array("[\"", "\"]"), array("[\n    \"", "\"\n]"), $json);
-        echo $json;
-    }
-}

+ 9 - 0
bin/update-spdx-licenses

@@ -0,0 +1,9 @@
+#!/usr/bin/env php
+<?php
+
+require __DIR__ . '/../src/bootstrap.php';
+
+use Composer\Util\SpdxLicensesUpdater;
+
+$licenses = new SpdxLicensesUpdater;
+$licenses->update();

+ 0 - 59
res/spdx-identifier.json

@@ -1,59 +0,0 @@
-[
-    "Glide", "Abstyles", "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0",
-    "AMPAS", "APL-1.0", "Adobe-Glyph", "APAFML", "Adobe-2006", "AGPL-1.0",
-    "Afmparse", "Aladdin", "ADSL", "AMDPLPA", "ANTLR-PD", "Apache-1.0",
-    "Apache-1.1", "Apache-2.0", "AML", "APSL-1.0", "APSL-1.1", "APSL-1.2",
-    "APSL-2.0", "Artistic-1.0", "Artistic-1.0-Perl", "Artistic-1.0-cl8",
-    "Artistic-2.0", "AAL", "Bahyph", "Barr", "Beerware", "BitTorrent-1.0",
-    "BitTorrent-1.1", "BSL-1.0", "Borceux", "BSD-2-Clause",
-    "BSD-2-Clause-FreeBSD", "BSD-2-Clause-NetBSD", "BSD-3-Clause",
-    "BSD-3-Clause-Clear", "BSD-4-Clause", "BSD-Protection",
-    "BSD-3-Clause-Attribution", "BSD-4-Clause-UC", "bzip2-1.0.5", "bzip2-1.0.6",
-    "Caldera", "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-B", "CECILL-C",
-    "ClArtistic", "MIT-CMU", "CNRI-Python", "CNRI-Python-GPL-Compatible",
-    "CPOL-1.02", "CDDL-1.0", "CDDL-1.1", "CPAL-1.0", "CPL-1.0", "CATOSL-1.1",
-    "Condor-1.1", "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.5", "CC-BY-3.0",
-    "CC-BY-4.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", "CC-BY-ND-3.0",
-    "CC-BY-ND-4.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5",
-    "CC-BY-NC-3.0", "CC-BY-NC-4.0", "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0",
-    "CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0", "CC-BY-NC-ND-4.0", "CC-BY-NC-SA-1.0",
-    "CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", "CC-BY-NC-SA-3.0", "CC-BY-NC-SA-4.0",
-    "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0",
-    "CC-BY-SA-4.0", "CC0-1.0", "Crossword", "CUA-OPL-1.0", "Cube", "D-FSL-1.0",
-    "diffmark", "WTFPL", "DOC", "Dotseqn", "DSDP", "dvipdfm", "EPL-1.0",
-    "eCos-2.0", "ECL-1.0", "ECL-2.0", "eGenix", "EFL-1.0", "EFL-2.0",
-    "MIT-advertising", "MIT-enna", "Entessa", "ErlPL-1.1", "EUDatagrid",
-    "EUPL-1.0", "EUPL-1.1", "Eurosym", "Fair", "MIT-feh", "Frameworx-1.0",
-    "FTL", "FSFUL", "FSFULLR", "Giftware", "GL2PS", "Glulxe", "AGPL-3.0",
-    "GFDL-1.1", "GFDL-1.2", "GFDL-1.3", "GPL-1.0", "GPL-1.0+", "GPL-2.0",
-    "GPL-2.0+", "GPL-2.0-with-autoconf-exception",
-    "GPL-2.0-with-bison-exception", "GPL-2.0-with-classpath-exception",
-    "GPL-2.0-with-font-exception", "GPL-2.0-with-GCC-exception", "GPL-3.0",
-    "GPL-3.0+", "GPL-3.0-with-autoconf-exception", "GPL-3.0-with-GCC-exception",
-    "LGPL-2.1", "LGPL-2.1+", "LGPL-3.0", "LGPL-3.0+", "LGPL-2.0", "LGPL-2.0+",
-    "gnuplot", "gSOAP-1.3b", "HaskellReport", "HPND", "IBM-pibs", "IPL-1.0",
-    "ImageMagick", "iMatix", "Imlib2", "IJG", "Intel-ACPI", "Intel", "IPA",
-    "ISC", "JasPer-2.0", "JSON", "LPPL-1.3a", "LPPL-1.0", "LPPL-1.1",
-    "LPPL-1.2", "LPPL-1.3c", "Latex2e", "BSD-3-Clause-LBNL", "Leptonica",
-    "Libpng", "libtiff", "LPL-1.02", "LPL-1.0", "MakeIndex", "MTLL", "MS-PL",
-    "MS-RL", "MirOS", "MITNFA", "MIT", "Motosoto", "MPL-1.0", "MPL-1.1",
-    "MPL-2.0", "MPL-2.0-no-copyleft-exception", "mpich2", "Multics", "Mup",
-    "NASA-1.3", "Naumen", "NBPL-1.0", "NetCDF", "NGPL", "NOSL", "NPL-1.0",
-    "NPL-1.1", "Newsletr", "NLPL", "Nokia", "NPOSL-3.0", "Noweb", "NRL", "NTP",
-    "Nunit", "OCLC-2.0", "ODbL-1.0", "PDDL-1.0", "OGTSL", "OLDAP-2.2.2",
-    "OLDAP-1.1", "OLDAP-1.2", "OLDAP-1.3", "OLDAP-1.4", "OLDAP-2.0",
-    "OLDAP-2.0.1", "OLDAP-2.1", "OLDAP-2.2", "OLDAP-2.2.1", "OLDAP-2.3",
-    "OLDAP-2.4", "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OML", "OPL-1.0",
-    "OSL-1.0", "OSL-1.1", "OSL-2.0", "OSL-2.1", "OSL-3.0", "OLDAP-2.8",
-    "OpenSSL", "PHP-3.0", "PHP-3.01", "Plexus", "PostgreSQL", "psfrag",
-    "psutils", "Python-2.0", "QPL-1.0", "Qhull", "Rdisc", "RPSL-1.0", "RPL-1.1",
-    "RPL-1.5", "RHeCos-1.1", "RSCPL", "Ruby", "SAX-PD", "Saxpath", "SCEA",
-    "SWL", "SGI-B-1.0", "SGI-B-1.1", "SGI-B-2.0", "OFL-1.0", "OFL-1.1",
-    "SimPL-2.0", "Sleepycat", "SNIA", "SMLNJ", "StandardML-NJ",
-    "SugarCRM-1.1.3", "SISSL", "SISSL-1.2", "SPL-1.0", "Watcom-1.0", "TCL",
-    "Unlicense", "TMate", "TORQUE-1.1", "TOSL", "Unicode-TOU", "NCSA", "Vim",
-    "VOSTROM", "VSL-1.0", "W3C", "Wsuipa", "WXwindows", "Xnet", "X11", "Xerox",
-    "XFree86-1.1", "xinetd", "xpp", "XSkat", "YPL-1.0", "YPL-1.1", "Zed",
-    "Zend-2.0", "Zimbra-1.3", "Zlib", "zlib-acknowledgement", "ZPL-1.1",
-    "ZPL-2.0", "ZPL-2.1"
-]

+ 1226 - 0
res/spdx-licenses.json

@@ -0,0 +1,1226 @@
+{
+    "Glide": [
+        "3dfx Glide License",
+        false
+    ],
+    "Abstyles": [
+        "Abstyles License",
+        false
+    ],
+    "AFL-1.1": [
+        "Academic Free License v1.1",
+        true
+    ],
+    "AFL-1.2": [
+        "Academic Free License v1.2",
+        true
+    ],
+    "AFL-2.0": [
+        "Academic Free License v2.0",
+        true
+    ],
+    "AFL-2.1": [
+        "Academic Free License v2.1",
+        true
+    ],
+    "AFL-3.0": [
+        "Academic Free License v3.0",
+        true
+    ],
+    "AMPAS": [
+        "Academy of Motion Picture Arts and Sciences BSD",
+        false
+    ],
+    "APL-1.0": [
+        "Adaptive Public License 1.0",
+        true
+    ],
+    "Adobe-Glyph": [
+        "Adobe Glyph List License",
+        false
+    ],
+    "APAFML": [
+        "Adobe Postscript AFM License",
+        false
+    ],
+    "Adobe-2006": [
+        "Adobe Systems Incorporated Source Code License Agreement",
+        false
+    ],
+    "AGPL-1.0": [
+        "Affero General Public License v1.0",
+        false
+    ],
+    "Afmparse": [
+        "Afmparse License",
+        false
+    ],
+    "Aladdin": [
+        "Aladdin Free Public License",
+        false
+    ],
+    "ADSL": [
+        "Amazon Digital Services License",
+        false
+    ],
+    "AMDPLPA": [
+        "AMD's plpa_map.c License",
+        false
+    ],
+    "ANTLR-PD": [
+        "ANTLR Software Rights Notice",
+        false
+    ],
+    "Apache-1.0": [
+        "Apache License 1.0",
+        false
+    ],
+    "Apache-1.1": [
+        "Apache License 1.1",
+        true
+    ],
+    "Apache-2.0": [
+        "Apache License 2.0",
+        true
+    ],
+    "AML": [
+        "Apple MIT License",
+        false
+    ],
+    "APSL-1.0": [
+        "Apple Public Source License 1.0",
+        true
+    ],
+    "APSL-1.1": [
+        "Apple Public Source License 1.1",
+        true
+    ],
+    "APSL-1.2": [
+        "Apple Public Source License 1.2",
+        true
+    ],
+    "APSL-2.0": [
+        "Apple Public Source License 2.0",
+        true
+    ],
+    "Artistic-1.0": [
+        "Artistic License 1.0",
+        true
+    ],
+    "Artistic-1.0-Perl": [
+        "Artistic License 1.0 (Perl)",
+        true
+    ],
+    "Artistic-1.0-cl8": [
+        "Artistic License 1.0 w/clause 8",
+        true
+    ],
+    "Artistic-2.0": [
+        "Artistic License 2.0",
+        true
+    ],
+    "AAL": [
+        "Attribution Assurance License",
+        true
+    ],
+    "Bahyph": [
+        "Bahyph License",
+        false
+    ],
+    "Barr": [
+        "Barr License",
+        false
+    ],
+    "Beerware": [
+        "Beerware License",
+        false
+    ],
+    "BitTorrent-1.0": [
+        "BitTorrent Open Source License v1.0",
+        false
+    ],
+    "BitTorrent-1.1": [
+        "BitTorrent Open Source License v1.1",
+        false
+    ],
+    "BSL-1.0": [
+        "Boost Software License 1.0",
+        true
+    ],
+    "Borceux": [
+        "Borceux license",
+        false
+    ],
+    "BSD-2-Clause": [
+        "BSD 2-clause \"Simplified\" License",
+        true
+    ],
+    "BSD-2-Clause-FreeBSD": [
+        "BSD 2-clause FreeBSD License",
+        false
+    ],
+    "BSD-2-Clause-NetBSD": [
+        "BSD 2-clause NetBSD License",
+        false
+    ],
+    "BSD-3-Clause": [
+        "BSD 3-clause \"New\" or \"Revised\" License",
+        true
+    ],
+    "BSD-3-Clause-Clear": [
+        "BSD 3-clause Clear License",
+        false
+    ],
+    "BSD-4-Clause": [
+        "BSD 4-clause \"Original\" or \"Old\" License",
+        false
+    ],
+    "BSD-Protection": [
+        "BSD Protection License",
+        false
+    ],
+    "BSD-3-Clause-Attribution": [
+        "BSD with attribution",
+        false
+    ],
+    "BSD-4-Clause-UC": [
+        "BSD-4-Clause (University of California-Specific)",
+        false
+    ],
+    "bzip2-1.0.5": [
+        "bzip2 and libbzip2 License v1.0.5",
+        false
+    ],
+    "bzip2-1.0.6": [
+        "bzip2 and libbzip2 License v1.0.6",
+        false
+    ],
+    "Caldera": [
+        "Caldera License",
+        false
+    ],
+    "CECILL-1.0": [
+        "CeCILL Free Software License Agreement v1.0",
+        false
+    ],
+    "CECILL-1.1": [
+        "CeCILL Free Software License Agreement v1.1",
+        false
+    ],
+    "CECILL-2.0": [
+        "CeCILL Free Software License Agreement v2.0",
+        false
+    ],
+    "CECILL-B": [
+        "CeCILL-B Free Software License Agreement",
+        false
+    ],
+    "CECILL-C": [
+        "CeCILL-C Free Software License Agreement",
+        false
+    ],
+    "ClArtistic": [
+        "Clarified Artistic License",
+        false
+    ],
+    "MIT-CMU": [
+        "CMU License",
+        false
+    ],
+    "CNRI-Python": [
+        "CNRI Python License",
+        true
+    ],
+    "CNRI-Python-GPL-Compatible": [
+        "CNRI Python Open Source GPL Compatible License Agreement",
+        false
+    ],
+    "CPOL-1.02": [
+        "Code Project Open License 1.02",
+        false
+    ],
+    "CDDL-1.0": [
+        "Common Development and Distribution License 1.0",
+        true
+    ],
+    "CDDL-1.1": [
+        "Common Development and Distribution License 1.1",
+        false
+    ],
+    "CPAL-1.0": [
+        "Common Public Attribution License 1.0",
+        true
+    ],
+    "CPL-1.0": [
+        "Common Public License 1.0",
+        true
+    ],
+    "CATOSL-1.1": [
+        "Computer Associates Trusted Open Source License 1.1",
+        true
+    ],
+    "Condor-1.1": [
+        "Condor Public License v1.1",
+        false
+    ],
+    "CC-BY-1.0": [
+        "Creative Commons Attribution 1.0",
+        false
+    ],
+    "CC-BY-2.0": [
+        "Creative Commons Attribution 2.0",
+        false
+    ],
+    "CC-BY-2.5": [
+        "Creative Commons Attribution 2.5",
+        false
+    ],
+    "CC-BY-3.0": [
+        "Creative Commons Attribution 3.0",
+        false
+    ],
+    "CC-BY-4.0": [
+        "Creative Commons Attribution 4.0",
+        false
+    ],
+    "CC-BY-ND-1.0": [
+        "Creative Commons Attribution No Derivatives 1.0",
+        false
+    ],
+    "CC-BY-ND-2.0": [
+        "Creative Commons Attribution No Derivatives 2.0",
+        false
+    ],
+    "CC-BY-ND-2.5": [
+        "Creative Commons Attribution No Derivatives 2.5",
+        false
+    ],
+    "CC-BY-ND-3.0": [
+        "Creative Commons Attribution No Derivatives 3.0",
+        false
+    ],
+    "CC-BY-ND-4.0": [
+        "Creative Commons Attribution No Derivatives 4.0",
+        false
+    ],
+    "CC-BY-NC-1.0": [
+        "Creative Commons Attribution Non Commercial 1.0",
+        false
+    ],
+    "CC-BY-NC-2.0": [
+        "Creative Commons Attribution Non Commercial 2.0",
+        false
+    ],
+    "CC-BY-NC-2.5": [
+        "Creative Commons Attribution Non Commercial 2.5",
+        false
+    ],
+    "CC-BY-NC-3.0": [
+        "Creative Commons Attribution Non Commercial 3.0",
+        false
+    ],
+    "CC-BY-NC-4.0": [
+        "Creative Commons Attribution Non Commercial 4.0",
+        false
+    ],
+    "CC-BY-NC-ND-1.0": [
+        "Creative Commons Attribution Non Commercial No Derivatives 1.0",
+        false
+    ],
+    "CC-BY-NC-ND-2.0": [
+        "Creative Commons Attribution Non Commercial No Derivatives 2.0",
+        false
+    ],
+    "CC-BY-NC-ND-2.5": [
+        "Creative Commons Attribution Non Commercial No Derivatives 2.5",
+        false
+    ],
+    "CC-BY-NC-ND-3.0": [
+        "Creative Commons Attribution Non Commercial No Derivatives 3.0",
+        false
+    ],
+    "CC-BY-NC-ND-4.0": [
+        "Creative Commons Attribution Non Commercial No Derivatives 4.0",
+        false
+    ],
+    "CC-BY-NC-SA-1.0": [
+        "Creative Commons Attribution Non Commercial Share Alike 1.0",
+        false
+    ],
+    "CC-BY-NC-SA-2.0": [
+        "Creative Commons Attribution Non Commercial Share Alike 2.0",
+        false
+    ],
+    "CC-BY-NC-SA-2.5": [
+        "Creative Commons Attribution Non Commercial Share Alike 2.5",
+        false
+    ],
+    "CC-BY-NC-SA-3.0": [
+        "Creative Commons Attribution Non Commercial Share Alike 3.0",
+        false
+    ],
+    "CC-BY-NC-SA-4.0": [
+        "Creative Commons Attribution Non Commercial Share Alike 4.0",
+        false
+    ],
+    "CC-BY-SA-1.0": [
+        "Creative Commons Attribution Share Alike 1.0",
+        false
+    ],
+    "CC-BY-SA-2.0": [
+        "Creative Commons Attribution Share Alike 2.0",
+        false
+    ],
+    "CC-BY-SA-2.5": [
+        "Creative Commons Attribution Share Alike 2.5",
+        false
+    ],
+    "CC-BY-SA-3.0": [
+        "Creative Commons Attribution Share Alike 3.0",
+        false
+    ],
+    "CC-BY-SA-4.0": [
+        "Creative Commons Attribution Share Alike 4.0",
+        false
+    ],
+    "CC0-1.0": [
+        "Creative Commons Zero v1.0 Universal",
+        false
+    ],
+    "Crossword": [
+        "Crossword License",
+        false
+    ],
+    "CUA-OPL-1.0": [
+        "CUA Office Public License v1.0",
+        true
+    ],
+    "Cube": [
+        "Cube License",
+        false
+    ],
+    "D-FSL-1.0": [
+        "Deutsche Freie Software Lizenz",
+        false
+    ],
+    "diffmark": [
+        "diffmark license",
+        false
+    ],
+    "WTFPL": [
+        "Do What The F*ck You Want To Public License",
+        false
+    ],
+    "DOC": [
+        "DOC License",
+        false
+    ],
+    "Dotseqn": [
+        "Dotseqn License",
+        false
+    ],
+    "DSDP": [
+        "DSDP License",
+        false
+    ],
+    "dvipdfm": [
+        "dvipdfm License",
+        false
+    ],
+    "EPL-1.0": [
+        "Eclipse Public License 1.0",
+        true
+    ],
+    "eCos-2.0": [
+        "eCos license version 2.0",
+        false
+    ],
+    "ECL-1.0": [
+        "Educational Community License v1.0",
+        true
+    ],
+    "ECL-2.0": [
+        "Educational Community License v2.0",
+        true
+    ],
+    "eGenix": [
+        "eGenix.com Public License 1.1.0",
+        false
+    ],
+    "EFL-1.0": [
+        "Eiffel Forum License v1.0",
+        true
+    ],
+    "EFL-2.0": [
+        "Eiffel Forum License v2.0",
+        true
+    ],
+    "MIT-advertising": [
+        "Enlightenment License (e16)",
+        false
+    ],
+    "MIT-enna": [
+        "enna License",
+        false
+    ],
+    "Entessa": [
+        "Entessa Public License v1.0",
+        true
+    ],
+    "ErlPL-1.1": [
+        "Erlang Public License v1.1",
+        false
+    ],
+    "EUDatagrid": [
+        "EU DataGrid Software License",
+        true
+    ],
+    "EUPL-1.0": [
+        "European Union Public License 1.0",
+        false
+    ],
+    "EUPL-1.1": [
+        "European Union Public License 1.1",
+        true
+    ],
+    "Eurosym": [
+        "Eurosym License",
+        false
+    ],
+    "Fair": [
+        "Fair License",
+        true
+    ],
+    "MIT-feh": [
+        "feh License",
+        false
+    ],
+    "Frameworx-1.0": [
+        "Frameworx Open License 1.0",
+        true
+    ],
+    "FTL": [
+        "Freetype Project License",
+        false
+    ],
+    "FSFUL": [
+        "FSF Unlimited License",
+        false
+    ],
+    "FSFULLR": [
+        "FSF Unlimited License (with License Retention)",
+        false
+    ],
+    "Giftware": [
+        "Giftware License",
+        false
+    ],
+    "GL2PS": [
+        "GL2PS License",
+        false
+    ],
+    "Glulxe": [
+        "Glulxe License",
+        false
+    ],
+    "AGPL-3.0": [
+        "GNU Affero General Public License v3.0",
+        true
+    ],
+    "GFDL-1.1": [
+        "GNU Free Documentation License v1.1",
+        false
+    ],
+    "GFDL-1.2": [
+        "GNU Free Documentation License v1.2",
+        false
+    ],
+    "GFDL-1.3": [
+        "GNU Free Documentation License v1.3",
+        false
+    ],
+    "GPL-1.0": [
+        "GNU General Public License v1.0 only",
+        false
+    ],
+    "GPL-1.0+": [
+        "GNU General Public License v1.0 or later",
+        false
+    ],
+    "GPL-2.0": [
+        "GNU General Public License v2.0 only",
+        true
+    ],
+    "GPL-2.0+": [
+        "GNU General Public License v2.0 or later",
+        true
+    ],
+    "GPL-2.0-with-autoconf-exception": [
+        "GNU General Public License v2.0 w/Autoconf exception",
+        true
+    ],
+    "GPL-2.0-with-bison-exception": [
+        "GNU General Public License v2.0 w/Bison exception",
+        true
+    ],
+    "GPL-2.0-with-classpath-exception": [
+        "GNU General Public License v2.0 w/Classpath exception",
+        true
+    ],
+    "GPL-2.0-with-font-exception": [
+        "GNU General Public License v2.0 w/Font exception",
+        true
+    ],
+    "GPL-2.0-with-GCC-exception": [
+        "GNU General Public License v2.0 w/GCC Runtime Library exception",
+        true
+    ],
+    "GPL-3.0": [
+        "GNU General Public License v3.0 only",
+        true
+    ],
+    "GPL-3.0+": [
+        "GNU General Public License v3.0 or later",
+        true
+    ],
+    "GPL-3.0-with-autoconf-exception": [
+        "GNU General Public License v3.0 w/Autoconf exception",
+        true
+    ],
+    "GPL-3.0-with-GCC-exception": [
+        "GNU General Public License v3.0 w/GCC Runtime Library exception",
+        true
+    ],
+    "LGPL-2.1": [
+        "GNU Lesser General Public License v2.1 only",
+        true
+    ],
+    "LGPL-2.1+": [
+        "GNU Lesser General Public License v2.1 or later",
+        true
+    ],
+    "LGPL-3.0": [
+        "GNU Lesser General Public License v3.0 only",
+        true
+    ],
+    "LGPL-3.0+": [
+        "GNU Lesser General Public License v3.0 or later",
+        true
+    ],
+    "LGPL-2.0": [
+        "GNU Library General Public License v2 only",
+        true
+    ],
+    "LGPL-2.0+": [
+        "GNU Library General Public License v2 or later",
+        true
+    ],
+    "gnuplot": [
+        "gnuplot License",
+        false
+    ],
+    "gSOAP-1.3b": [
+        "gSOAP Public License v1.3b",
+        false
+    ],
+    "HaskellReport": [
+        "Haskell Language Report License",
+        false
+    ],
+    "HPND": [
+        "Historic Permission Notice and Disclaimer",
+        true
+    ],
+    "IBM-pibs": [
+        "IBM PowerPC Initialization and Boot Software",
+        false
+    ],
+    "IPL-1.0": [
+        "IBM Public License v1.0",
+        true
+    ],
+    "ImageMagick": [
+        "ImageMagick License",
+        false
+    ],
+    "iMatix": [
+        "iMatix Standard Function Library Agreement",
+        false
+    ],
+    "Imlib2": [
+        "Imlib2 License",
+        false
+    ],
+    "IJG": [
+        "Independent JPEG Group License",
+        false
+    ],
+    "Intel-ACPI": [
+        "Intel ACPI Software License Agreement",
+        false
+    ],
+    "Intel": [
+        "Intel Open Source License",
+        true
+    ],
+    "IPA": [
+        "IPA Font License",
+        true
+    ],
+    "ISC": [
+        "ISC License",
+        true
+    ],
+    "JasPer-2.0": [
+        "JasPer License",
+        false
+    ],
+    "JSON": [
+        "JSON License",
+        false
+    ],
+    "LPPL-1.3a": [
+        "LaTeX Project Public License 1.3a",
+        false
+    ],
+    "LPPL-1.0": [
+        "LaTeX Project Public License v1.0",
+        false
+    ],
+    "LPPL-1.1": [
+        "LaTeX Project Public License v1.1",
+        false
+    ],
+    "LPPL-1.2": [
+        "LaTeX Project Public License v1.2",
+        false
+    ],
+    "LPPL-1.3c": [
+        "LaTeX Project Public License v1.3c",
+        true
+    ],
+    "Latex2e": [
+        "Latex2e License",
+        false
+    ],
+    "BSD-3-Clause-LBNL": [
+        "Lawrence Berkeley National Labs BSD variant license",
+        false
+    ],
+    "Leptonica": [
+        "Leptonica License",
+        false
+    ],
+    "Libpng": [
+        "libpng License",
+        false
+    ],
+    "libtiff": [
+        "libtiff License",
+        false
+    ],
+    "LPL-1.02": [
+        "Lucent Public License v1.02",
+        true
+    ],
+    "LPL-1.0": [
+        "Lucent Public License Version 1.0",
+        true
+    ],
+    "MakeIndex": [
+        "MakeIndex License",
+        false
+    ],
+    "MTLL": [
+        "Matrix Template Library License",
+        false
+    ],
+    "MS-PL": [
+        "Microsoft Public License",
+        true
+    ],
+    "MS-RL": [
+        "Microsoft Reciprocal License",
+        true
+    ],
+    "MirOS": [
+        "MirOS Licence",
+        true
+    ],
+    "MITNFA": [
+        "MIT +no-false-attribs license",
+        false
+    ],
+    "MIT": [
+        "MIT License",
+        true
+    ],
+    "Motosoto": [
+        "Motosoto License",
+        true
+    ],
+    "MPL-1.0": [
+        "Mozilla Public License 1.0",
+        true
+    ],
+    "MPL-1.1": [
+        "Mozilla Public License 1.1",
+        true
+    ],
+    "MPL-2.0": [
+        "Mozilla Public License 2.0",
+        true
+    ],
+    "MPL-2.0-no-copyleft-exception": [
+        "Mozilla Public License 2.0 (no copyleft exception)",
+        true
+    ],
+    "mpich2": [
+        "mpich2 License",
+        false
+    ],
+    "Multics": [
+        "Multics License",
+        true
+    ],
+    "Mup": [
+        "Mup License",
+        false
+    ],
+    "NASA-1.3": [
+        "NASA Open Source Agreement 1.3",
+        true
+    ],
+    "Naumen": [
+        "Naumen Public License",
+        true
+    ],
+    "NBPL-1.0": [
+        "Net Boolean Public License v1",
+        false
+    ],
+    "NetCDF": [
+        "NetCDF license",
+        false
+    ],
+    "NGPL": [
+        "Nethack General Public License",
+        true
+    ],
+    "NOSL": [
+        "Netizen Open Source License",
+        false
+    ],
+    "NPL-1.0": [
+        "Netscape Public License v1.0",
+        false
+    ],
+    "NPL-1.1": [
+        "Netscape Public License v1.1",
+        false
+    ],
+    "Newsletr": [
+        "Newsletr License",
+        false
+    ],
+    "NLPL": [
+        "No Limit Public License",
+        false
+    ],
+    "Nokia": [
+        "Nokia Open Source License",
+        true
+    ],
+    "NPOSL-3.0": [
+        "Non-Profit Open Software License 3.0",
+        true
+    ],
+    "Noweb": [
+        "Noweb License",
+        false
+    ],
+    "NRL": [
+        "NRL License",
+        false
+    ],
+    "NTP": [
+        "NTP License",
+        true
+    ],
+    "Nunit": [
+        "Nunit License",
+        false
+    ],
+    "OCLC-2.0": [
+        "OCLC Research Public License 2.0",
+        true
+    ],
+    "ODbL-1.0": [
+        "ODC Open Database License v1.0",
+        false
+    ],
+    "PDDL-1.0": [
+        "ODC Public Domain Dedication & License 1.0",
+        false
+    ],
+    "OGTSL": [
+        "Open Group Test Suite License",
+        true
+    ],
+    "OLDAP-2.2.2": [
+        "Open LDAP Public License  2.2.2",
+        false
+    ],
+    "OLDAP-1.1": [
+        "Open LDAP Public License v1.1",
+        false
+    ],
+    "OLDAP-1.2": [
+        "Open LDAP Public License v1.2",
+        false
+    ],
+    "OLDAP-1.3": [
+        "Open LDAP Public License v1.3",
+        false
+    ],
+    "OLDAP-1.4": [
+        "Open LDAP Public License v1.4",
+        false
+    ],
+    "OLDAP-2.0": [
+        "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)",
+        false
+    ],
+    "OLDAP-2.0.1": [
+        "Open LDAP Public License v2.0.1",
+        false
+    ],
+    "OLDAP-2.1": [
+        "Open LDAP Public License v2.1",
+        false
+    ],
+    "OLDAP-2.2": [
+        "Open LDAP Public License v2.2",
+        false
+    ],
+    "OLDAP-2.2.1": [
+        "Open LDAP Public License v2.2.1",
+        false
+    ],
+    "OLDAP-2.3": [
+        "Open LDAP Public License v2.3",
+        false
+    ],
+    "OLDAP-2.4": [
+        "Open LDAP Public License v2.4",
+        false
+    ],
+    "OLDAP-2.5": [
+        "Open LDAP Public License v2.5",
+        false
+    ],
+    "OLDAP-2.6": [
+        "Open LDAP Public License v2.6",
+        false
+    ],
+    "OLDAP-2.7": [
+        "Open LDAP Public License v2.7",
+        false
+    ],
+    "OML": [
+        "Open Market License",
+        false
+    ],
+    "OPL-1.0": [
+        "Open Public License v1.0",
+        false
+    ],
+    "OSL-1.0": [
+        "Open Software License 1.0",
+        true
+    ],
+    "OSL-1.1": [
+        "Open Software License 1.1",
+        false
+    ],
+    "OSL-2.0": [
+        "Open Software License 2.0",
+        true
+    ],
+    "OSL-2.1": [
+        "Open Software License 2.1",
+        true
+    ],
+    "OSL-3.0": [
+        "Open Software License 3.0",
+        true
+    ],
+    "OLDAP-2.8": [
+        "OpenLDAP Public License v2.8",
+        false
+    ],
+    "OpenSSL": [
+        "OpenSSL License",
+        false
+    ],
+    "PHP-3.0": [
+        "PHP License v3.0",
+        true
+    ],
+    "PHP-3.01": [
+        "PHP License v3.01",
+        false
+    ],
+    "Plexus": [
+        "Plexus Classworlds License",
+        false
+    ],
+    "PostgreSQL": [
+        "PostgreSQL License",
+        true
+    ],
+    "psfrag": [
+        "psfrag License",
+        false
+    ],
+    "psutils": [
+        "psutils License",
+        false
+    ],
+    "Python-2.0": [
+        "Python License 2.0",
+        true
+    ],
+    "QPL-1.0": [
+        "Q Public License 1.0",
+        true
+    ],
+    "Qhull": [
+        "Qhull License",
+        false
+    ],
+    "Rdisc": [
+        "Rdisc License",
+        false
+    ],
+    "RPSL-1.0": [
+        "RealNetworks Public Source License v1.0",
+        true
+    ],
+    "RPL-1.1": [
+        "Reciprocal Public License 1.1",
+        true
+    ],
+    "RPL-1.5": [
+        "Reciprocal Public License 1.5",
+        true
+    ],
+    "RHeCos-1.1": [
+        "Red Hat eCos Public License v1.1",
+        false
+    ],
+    "RSCPL": [
+        "Ricoh Source Code Public License",
+        true
+    ],
+    "Ruby": [
+        "Ruby License",
+        false
+    ],
+    "SAX-PD": [
+        "Sax Public Domain Notice",
+        false
+    ],
+    "Saxpath": [
+        "Saxpath License",
+        false
+    ],
+    "SCEA": [
+        "SCEA Shared Source License",
+        false
+    ],
+    "SWL": [
+        "Scheme Widget Library (SWL) Software License Agreement",
+        false
+    ],
+    "SGI-B-1.0": [
+        "SGI Free Software License B v1.0",
+        false
+    ],
+    "SGI-B-1.1": [
+        "SGI Free Software License B v1.1",
+        false
+    ],
+    "SGI-B-2.0": [
+        "SGI Free Software License B v2.0",
+        false
+    ],
+    "OFL-1.0": [
+        "SIL Open Font License 1.0",
+        false
+    ],
+    "OFL-1.1": [
+        "SIL Open Font License 1.1",
+        true
+    ],
+    "SimPL-2.0": [
+        "Simple Public License 2.0",
+        true
+    ],
+    "Sleepycat": [
+        "Sleepycat License",
+        true
+    ],
+    "SNIA": [
+        "SNIA Public License 1.1",
+        false
+    ],
+    "SMLNJ": [
+        "Standard ML of New Jersey License",
+        false
+    ],
+    "StandardML-NJ": [
+        "Standard ML of New Jersey License",
+        false
+    ],
+    "SugarCRM-1.1.3": [
+        "SugarCRM Public License v1.1.3",
+        false
+    ],
+    "SISSL": [
+        "Sun Industry Standards Source License v1.1",
+        true
+    ],
+    "SISSL-1.2": [
+        "Sun Industry Standards Source License v1.2",
+        false
+    ],
+    "SPL-1.0": [
+        "Sun Public License v1.0",
+        true
+    ],
+    "Watcom-1.0": [
+        "Sybase Open Watcom Public License 1.0",
+        true
+    ],
+    "TCL": [
+        "TCL/TK License",
+        false
+    ],
+    "Unlicense": [
+        "The Unlicense",
+        false
+    ],
+    "TMate": [
+        "TMate Open Source License",
+        false
+    ],
+    "TORQUE-1.1": [
+        "TORQUE v2.5+ Software License v1.1",
+        false
+    ],
+    "TOSL": [
+        "Trusster Open Source License",
+        false
+    ],
+    "Unicode-TOU": [
+        "Unicode Terms of Use",
+        false
+    ],
+    "NCSA": [
+        "University of Illinois/NCSA Open Source License",
+        true
+    ],
+    "Vim": [
+        "Vim License",
+        false
+    ],
+    "VOSTROM": [
+        "VOSTROM Public License for Open Source",
+        false
+    ],
+    "VSL-1.0": [
+        "Vovida Software License v1.0",
+        true
+    ],
+    "W3C": [
+        "W3C Software Notice and License",
+        true
+    ],
+    "Wsuipa": [
+        "Wsuipa License",
+        false
+    ],
+    "WXwindows": [
+        "wxWindows Library License",
+        true
+    ],
+    "Xnet": [
+        "X.Net License",
+        true
+    ],
+    "X11": [
+        "X11 License",
+        false
+    ],
+    "Xerox": [
+        "Xerox License",
+        false
+    ],
+    "XFree86-1.1": [
+        "XFree86 License 1.1",
+        false
+    ],
+    "xinetd": [
+        "xinetd License",
+        false
+    ],
+    "xpp": [
+        "XPP License",
+        false
+    ],
+    "XSkat": [
+        "XSkat License",
+        false
+    ],
+    "YPL-1.0": [
+        "Yahoo! Public License v1.0",
+        false
+    ],
+    "YPL-1.1": [
+        "Yahoo! Public License v1.1",
+        false
+    ],
+    "Zed": [
+        "Zed License",
+        false
+    ],
+    "Zend-2.0": [
+        "Zend License v2.0",
+        false
+    ],
+    "Zimbra-1.3": [
+        "Zimbra Public License v1.3",
+        false
+    ],
+    "Zlib": [
+        "zlib License",
+        true
+    ],
+    "zlib-acknowledgement": [
+        "zlib/libpng License with Acknowledgement",
+        false
+    ],
+    "ZPL-1.1": [
+        "Zope Public License 1.1",
+        false
+    ],
+    "ZPL-2.0": [
+        "Zope Public License 2.0",
+        true
+    ],
+    "ZPL-2.1": [
+        "Zope Public License 2.1",
+        false
+    ]
+}

+ 37 - 13
src/Composer/Command/ShowCommand.php

@@ -28,6 +28,7 @@ use Composer\Repository\CompositeRepository;
 use Composer\Repository\ComposerRepository;
 use Composer\Repository\PlatformRepository;
 use Composer\Repository\RepositoryInterface;
+use Composer\Util\SpdxLicense;
 
 /**
  * @author Robert Schönthal <seroscho@googlemail.com>
@@ -115,18 +116,18 @@ EOT
                 $versions = array($package->getPrettyVersion() => $package->getVersion());
             }
 
-            $this->printMeta($input, $output, $package, $versions, $installedRepo, $repos);
-            $this->printLinks($input, $output, $package, 'requires');
-            $this->printLinks($input, $output, $package, 'devRequires', 'requires (dev)');
+            $this->printMeta($package, $versions, $installedRepo);
+            $this->printLinks($package, 'requires');
+            $this->printLinks($package, 'devRequires', 'requires (dev)');
             if ($package->getSuggests()) {
                 $this->getIO()->write("\n<info>suggests</info>");
                 foreach ($package->getSuggests() as $suggested => $reason) {
                     $this->getIO()->write($suggested . ' <comment>' . $reason . '</comment>');
                 }
             }
-            $this->printLinks($input, $output, $package, 'provides');
-            $this->printLinks($input, $output, $package, 'conflicts');
-            $this->printLinks($input, $output, $package, 'replaces');
+            $this->printLinks($package, 'provides');
+            $this->printLinks($package, 'conflicts');
+            $this->printLinks($package, 'replaces');
 
             return;
         }
@@ -283,14 +284,14 @@ EOT
     /**
      * prints package meta data
      */
-    protected function printMeta(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos)
+    protected function printMeta(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo)
     {
         $this->getIO()->write('<info>name</info>     : ' . $package->getPrettyName());
         $this->getIO()->write('<info>descrip.</info> : ' . $package->getDescription());
         $this->getIO()->write('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
-        $this->printVersions($input, $output, $package, $versions, $installedRepo, $repos);
+        $this->printVersions($package, $versions, $installedRepo);
         $this->getIO()->write('<info>type</info>     : ' . $package->getType());
-        $this->getIO()->write('<info>license</info>  : ' . implode(', ', $package->getLicense()));
+        $this->printLicenses($package);
         $this->getIO()->write('<info>source</info>   : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
         $this->getIO()->write('<info>dist</info>     : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
         $this->getIO()->write('<info>names</info>    : ' . implode(', ', $package->getNames()));
@@ -339,7 +340,7 @@ EOT
     /**
      * prints all available versions of this package and highlights the installed one if any
      */
-    protected function printVersions(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos)
+    protected function printVersions(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo)
     {
         uasort($versions, 'version_compare');
         $versions = array_keys(array_reverse($versions));
@@ -361,13 +362,11 @@ EOT
     /**
      * print link objects
      *
-     * @param InputInterface           $input
-     * @param OutputInterface          $output
      * @param CompletePackageInterface $package
      * @param string                   $linkType
      * @param string                   $title
      */
-    protected function printLinks(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, $linkType, $title = null)
+    protected function printLinks(CompletePackageInterface $package, $linkType, $title = null)
     {
         $title = $title ?: $linkType;
         if ($links = $package->{'get'.ucfirst($linkType)}()) {
@@ -378,4 +377,29 @@ EOT
             }
         }
     }
+
+    /**
+     * Prints the licenses of a package with metadata
+     *
+     * @param CompletePackageInterface $package
+     */
+    protected function printLicenses(CompletePackageInterface $package)
+    {
+        $spdxLicense = new SpdxLicense;
+
+        $licenses = $package->getLicense();
+
+        foreach($licenses as $licenseId) {
+            $license = $spdxLicense->getLicenseByIdentifier($licenseId); // keys: 0 fullname, 1 osi, 2 url
+
+            // is license OSI approved?
+            if($license[1] === true) {
+                $out = sprintf('%s (%s) (OSI approved) %s', $license[0], $licenseId, $license[2]);
+            } else {
+                $out = sprintf('%s (%s) %s', $license[0], $licenseId, $license[2]);
+            }
+
+            $this->getIO()->write('<info>license</info>  : ' . $out);
+        }
+    }
 }

+ 1 - 1
src/Composer/Util/ConfigValidator.php

@@ -82,7 +82,7 @@ class ConfigValidator
                 }
             }
 
-            $licenseValidator = new SpdxLicenseIdentifier();
+            $licenseValidator = new SpdxLicense();
             if ('proprietary' !== $manifest['license'] && array() !== $manifest['license'] && !$licenseValidator->validate($manifest['license'])) {
                 $warnings[] = sprintf(
                     'License %s is not a valid SPDX license identifier, see http://www.spdx.org/licenses/ if you use an open license.'

+ 229 - 178
src/Composer/Util/SpdxLicenseIdentifier.php → src/Composer/Util/SpdxLicense.php

@@ -1,178 +1,229 @@
-<?php
-
-/*
- * This file is part of Composer.
- *
- * (c) Nils Adermann <naderman@naderman.de>
- *     Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Composer\Util;
-
-use Composer\Json\JsonFile;
-
-/**
- * Supports composer array and SPDX tag notation for disjunctive/conjunctive
- * licenses.
- *
- * @author Tom Klingenberg <tklingenberg@lastflood.net>
- */
-class SpdxLicenseIdentifier
-{
-    /**
-     * @var array
-     */
-    private $identifiers;
-
-    public function __construct()
-    {
-        $this->initIdentifiers();
-    }
-
-    /**
-     * @param array|string $license
-     *
-     * @return bool
-     * @throws \InvalidArgumentException
-     */
-    public function validate($license)
-    {
-        if (is_array($license)) {
-            $count = count($license);
-            if ($count !== count(array_filter($license, 'is_string'))) {
-                throw new \InvalidArgumentException('Array of strings expected.');
-            }
-            $license = $count > 1  ? '('.implode(' or ', $license).')' : (string) reset($license);
-        }
-        if (!is_string($license)) {
-            throw new \InvalidArgumentException(sprintf(
-                'Array or String expected, %s given.', gettype($license)
-            ));
-        }
-
-        return $this->isValidLicenseString($license);
-    }
-
-    /**
-     * Loads SPDX identifiers
-     */
-    private function initIdentifiers()
-    {
-        $jsonFile = new JsonFile(__DIR__ . '/../../../res/spdx-identifier.json');
-        $this->identifiers = $jsonFile->read();
-    }
-
-    /**
-     * @param string $identifier
-     *
-     * @return bool
-     */
-    private function isValidLicenseIdentifier($identifier)
-    {
-        return in_array($identifier, $this->identifiers);
-    }
-
-    /**
-     * @param string $license
-     *
-     * @return bool
-     * @throws \RuntimeException
-     */
-    private function isValidLicenseString($license)
-    {
-        $tokens = array(
-            'po' => '\(',
-            'pc' => '\)',
-            'op' => '(?:or|and)',
-            'lix' => '(?:NONE|NOASSERTION)',
-            'lir' => 'LicenseRef-\d+',
-            'lic' => '[-+_.a-zA-Z0-9]{3,}',
-            'ws' => '\s+',
-            '_' => '.',
-        );
-
-        $next = function () use ($license, $tokens) {
-            static $offset = 0;
-
-            if ($offset >= strlen($license)) {
-                return null;
-            }
-
-            foreach ($tokens as $name => $token) {
-                if (false === $r = preg_match('{' . $token . '}', $license, $matches, PREG_OFFSET_CAPTURE, $offset)) {
-                    throw new \RuntimeException('Pattern for token %s failed (regex error).', $name);
-                }
-                if ($r === 0) {
-                    continue;
-                }
-                if ($matches[0][1] !== $offset) {
-                    continue;
-                }
-                $offset += strlen($matches[0][0]);
-
-                return array($name, $matches[0][0]);
-            }
-
-            throw new \RuntimeException('At least the last pattern needs to match, but it did not (dot-match-all is missing?).');
-        };
-
-        $open = 0;
-        $require = 1;
-        $lastop = null;
-
-        while (list($token, $string) = $next()) {
-            switch ($token) {
-                case 'po':
-                    if ($open || !$require) {
-                        return false;
-                    }
-                    $open = 1;
-                    break;
-                case 'pc':
-                    if ($open !== 1 || $require || !$lastop) {
-                        return false;
-                    }
-                    $open = 2;
-                    break;
-                case 'op':
-                    if ($require || !$open) {
-                        return false;
-                    }
-                    $lastop || $lastop = $string;
-                    if ($lastop !== $string) {
-                        return false;
-                    }
-                    $require = 1;
-                    break;
-                case 'lix':
-                    if ($open) {
-                        return false;
-                    }
-                    goto lir;
-                case 'lic':
-                    if (!$this->isValidLicenseIdentifier($string)) {
-                        return false;
-                    }
-                    // Fall-through intended
-                case 'lir':
-                    lir:
-                    if (!$require) {
-                        return false;
-                    }
-                    $require = 0;
-                    break;
-                case 'ws':
-                    break;
-                case '_':
-                    return false;
-                default:
-                    throw new \RuntimeException(sprintf('Unparsed token: %s.', print_r($token, true)));
-            }
-        }
-
-        return !($open % 2 || $require);
-    }
-}
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+use Composer\Json\JsonFile;
+
+/**
+ * Supports composer array and SPDX tag notation for disjunctive/conjunctive
+ * licenses.
+ *
+ * @author Tom Klingenberg <tklingenberg@lastflood.net>
+ */
+class SpdxLicense
+{
+    /**
+     * @var array
+     */
+    private $licenses;
+
+    public function __construct()
+    {
+        $this->loadLicenses();
+    }
+
+    private function loadLicenses()
+    {
+        if(is_array($this->licenses)) {
+            return $this->licenses;
+        }
+
+        $jsonFile = new JsonFile(__DIR__ . '/../../../res/spdx-licenses.json');
+        $this->licenses = $jsonFile->read();
+
+        return $this->licenses;
+    }
+
+    /**
+     * Returns license metadata by license identifier.
+     *
+     * @param string $identifier
+     *
+     * @return array
+     */
+    public function getLicenseByIdentifier($identifier)
+    {
+        $license = $this->licenses[$identifier];
+
+        // add URL for the license text (it's not included in the json)
+        $license[2] = 'http://spdx.org/licenses/' . $identifier . '#licenseText';
+
+        return $license;
+    }
+
+    /**
+     * Returns the short identifier of a license by full name.
+     *
+     * @param string $identifier
+     *
+     * @return string
+     */
+    public function getIdentifierByName($name)
+    {
+        foreach ($this->licenses as $identifier => $licenseData) {
+            if($licenseData[0] === $name) { // key 0 = fullname
+                return $identifier;
+            }
+        }
+    }
+
+    /**
+     * Returns the OSI Approved status for a license by identifier.
+     *
+     * @return bool
+     */
+    public function isOsiApprovedByIdentifier($identifier)
+    {
+        return $this->licenses[$identifier][1]; // key 1 = osi approved
+    }
+
+    /**
+     * Check, if the identifier for a license is valid.
+     * 
+     * @param string $identifier
+     *
+     * @return bool
+     */
+    private function isValidLicenseIdentifier($identifier)
+    {
+        $identifiers = array_keys($this->licenses);
+
+        return in_array($identifier, $identifiers);
+    }
+
+    /**
+     * @param array|string $license
+     *
+     * @return bool
+     * @throws \InvalidArgumentException
+     */
+    public function validate($license)
+    {
+        if (is_array($license)) {
+            $count = count($license);
+            if ($count !== count(array_filter($license, 'is_string'))) {
+                throw new \InvalidArgumentException('Array of strings expected.');
+            }
+            $license = $count > 1  ? '('.implode(' or ', $license).')' : (string) reset($license);
+        }
+
+        if (!is_string($license)) {
+            throw new \InvalidArgumentException(sprintf(
+                'Array or String expected, %s given.', gettype($license)
+            ));
+        }
+
+        return $this->isValidLicenseString($license);
+    }
+
+    /**
+     * @param string $license
+     *
+     * @return bool
+     * @throws \RuntimeException
+     */
+    private function isValidLicenseString($license)
+    {
+        $tokens = array(
+            'po' => '\(',
+            'pc' => '\)',
+            'op' => '(?:or|and)',
+            'lix' => '(?:NONE|NOASSERTION)',
+            'lir' => 'LicenseRef-\d+',
+            'lic' => '[-+_.a-zA-Z0-9]{3,}',
+            'ws' => '\s+',
+            '_' => '.',
+        );
+
+        $next = function () use ($license, $tokens) {
+            static $offset = 0;
+
+            if ($offset >= strlen($license)) {
+                return null;
+            }
+
+            foreach ($tokens as $name => $token) {
+                if (false === $r = preg_match('{' . $token . '}', $license, $matches, PREG_OFFSET_CAPTURE, $offset)) {
+                    throw new \RuntimeException('Pattern for token %s failed (regex error).', $name);
+                }
+                if ($r === 0) {
+                    continue;
+                }
+                if ($matches[0][1] !== $offset) {
+                    continue;
+                }
+                $offset += strlen($matches[0][0]);
+
+                return array($name, $matches[0][0]);
+            }
+
+            throw new \RuntimeException('At least the last pattern needs to match, but it did not (dot-match-all is missing?).');
+        };
+
+        $open = 0;
+        $require = 1;
+        $lastop = null;
+
+        while (list($token, $string) = $next()) {
+            switch ($token) {
+                case 'po':
+                    if ($open || !$require) {
+                        return false;
+                    }
+                    $open = 1;
+                    break;
+                case 'pc':
+                    if ($open !== 1 || $require || !$lastop) {
+                        return false;
+                    }
+                    $open = 2;
+                    break;
+                case 'op':
+                    if ($require || !$open) {
+                        return false;
+                    }
+                    $lastop || $lastop = $string;
+                    if ($lastop !== $string) {
+                        return false;
+                    }
+                    $require = 1;
+                    break;
+                case 'lix':
+                    if ($open) {
+                        return false;
+                    }
+                    goto lir;
+                case 'lic':
+                    if (!$this->isValidLicenseIdentifier($string)) {
+                        return false;
+                    }
+                    // Fall-through intended
+                case 'lir':
+                    lir:
+                    if (!$require) {
+                        return false;
+                    }
+                    $require = 0;
+                    break;
+                case 'ws':
+                    break;
+                case '_':
+                    return false;
+                default:
+                    throw new \RuntimeException(sprintf('Unparsed token: %s.', print_r($token, true)));
+            }
+        }
+
+        return !($open % 2 || $require);
+    }
+}

+ 67 - 0
src/Composer/Util/SpdxLicensesUpdater.php

@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+use Composer\Json\JsonFormatter;
+
+/**
+ * The SPDX Licenses Updater scrapes licenses from the spdx website
+ * and updates the "res/spdx-licenses.json" file accordingly.
+ *
+ * The class is used by the update script "bin/update-spdx-licenses".
+ */
+class SpdxLicensesUpdater
+{
+    private $licensesUrl = 'http://www.spdx.org/licenses/';
+
+    public function update()
+    {
+        $json = json_encode($this->getLicenses(), true);
+        $prettyJson = JsonFormatter::format($json, true, true);
+        file_put_contents(__DIR__ . '/../../../res/spdx-licenses.json', $prettyJson);
+    }
+
+    private function getLicenses()
+    {
+        $licenses = array();
+
+        $dom = new \DOMDocument;
+        $dom->loadHTMLFile($this->licensesUrl);
+
+        $xPath = new \DOMXPath($dom);
+        $trs = $xPath->query('//table//tbody//tr');
+
+        // iterate over each row in the table
+        foreach($trs as $tr) {
+            $tds = $tr->getElementsByTagName('td'); // get the columns in this row
+
+            if($tds->length < 4) {
+                throw new \Exception('Obtaining the license table failed. Wrong table format. Found less than 4 cells in a row.');
+            }
+
+            if(trim($tds->item(3)->nodeValue) == 'License Text') {
+                $fullname    = trim($tds->item(0)->nodeValue);
+                $identifier  = trim($tds->item(1)->nodeValue);
+                $osiApproved = ((isset($tds->item(2)->nodeValue) && $tds->item(2)->nodeValue === 'Y')) ? true : false;
+
+                // The license URL is not scraped intentionally to keep json file size low.
+                // It's build when requested, see SpdxLicense->getLicenseByIdentifier().
+                //$licenseURL = = $tds->item(3)->getAttribute('href');
+
+                $licenses += array($identifier => array($fullname, $osiApproved));
+            }
+        }
+
+        return $licenses;
+    }
+}

+ 47 - 9
tests/Composer/Test/Util/SpdxLicenseIdentifierTest.php → tests/Composer/Test/Util/SpdxLicenseTest.php

@@ -2,12 +2,28 @@
 namespace Composer\Test\Util;
 
 use Composer\TestCase;
-use Composer\Util\SpdxLicenseIdentifier;
+use Composer\Util\SpdxLicense;
 
-class SpdxLicenseIdentifierTest extends TestCase
+class SpdxLicenseTest extends TestCase
 {
+    /**
+     * @var object
+     */
+    private $license;
+
+    public function setUp()
+    {
+        $this->license = new SpdxLicense;
+    }
+
     public static function provideValidLicenses()
     {
+        $json = file_get_contents(__DIR__ . '/../../../../res/spdx-licenses.json');
+
+        $licenses = json_decode($json, true);
+
+        $identifiers = array_keys($licenses);
+
         $valid = array_merge(
             array(
                 "MIT",
@@ -18,7 +34,7 @@ class SpdxLicenseIdentifierTest extends TestCase
                 "(LGPL-2.0 or GPL-3.0+)",
                 "(EUDatagrid and GPL-3.0+)",
             ),
-            json_decode(file_get_contents(__DIR__ . '/../../../../res/spdx-identifier.json'))
+            $identifiers
         );
 
         foreach ($valid as &$r) {
@@ -68,8 +84,7 @@ class SpdxLicenseIdentifierTest extends TestCase
      */
     public function testValidate($license)
     {
-        $validator = new SpdxLicenseIdentifier();
-        $this->assertTrue($validator->validate($license));
+        $this->assertTrue($this->license->validate($license));
     }
 
     /**
@@ -78,8 +93,7 @@ class SpdxLicenseIdentifierTest extends TestCase
      */
     public function testInvalidLicenses($invalidLicense)
     {
-        $validator = new SpdxLicenseIdentifier();
-        $this->assertFalse($validator->validate($invalidLicense));
+        $this->assertFalse($this->license->validate($invalidLicense));
     }
 
     /**
@@ -88,7 +102,31 @@ class SpdxLicenseIdentifierTest extends TestCase
      */
     public function testInvalidArgument($invalidArgument)
     {
-        $validator = new SpdxLicenseIdentifier();
-        $validator->validate($invalidArgument);
+        $this->license->validate($invalidArgument);
+    }
+
+    public function testGetLicenseByIdentifier()
+    {
+        $license = $this->license->getLicenseByIdentifier('AGPL-1.0');
+        $this->assertEquals($license[0], 'Affero General Public License v1.0'); // fullname
+        $this->assertFalse($license[1]); // osi approved
+    }
+
+    public function testGetIdentifierByName()
+    {
+        $identifier = $this->license->getIdentifierByName('Affero General Public License v1.0');
+        $this->assertEquals($identifier, 'AGPL-1.0');
+
+        $identifier = $this->license->getIdentifierByName('BSD 2-clause "Simplified" License');
+        $this->assertEquals($identifier, 'BSD-2-Clause');
+    }
+
+    public function testIsOsiApprovedByIdentifier()
+    {
+        $osiApproved = $this->license->isOsiApprovedByIdentifier('MIT');
+        $this->assertTrue($osiApproved);
+
+        $osiApproved = $this->license->isOsiApprovedByIdentifier('AGPL-1.0');
+        $this->assertFalse($osiApproved);
     }
 }