diff --git a/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java b/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java index 236affbf0..7e1f4513f 100644 --- a/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java +++ b/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java @@ -30,6 +30,7 @@ package cc.arduino.contributions.packages.ui; import cc.arduino.contributions.DownloadableContribution; +import cc.arduino.contributions.GPGDetachedSignatureVerifier; import cc.arduino.contributions.packages.ContributedPlatform; import cc.arduino.contributions.packages.ContributionInstaller; import cc.arduino.contributions.packages.ContributionsIndexer; @@ -116,7 +117,7 @@ public class ContributionManagerUI extends InstallerJDialog { } // Create ConstributionInstaller tied with the provided index - installer = new ContributionInstaller(indexer, platform) { + installer = new ContributionInstaller(indexer, platform, new GPGDetachedSignatureVerifier()) { @Override public void onProgress(Progress progress) { setProgress(progress); diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 564341314..b3410b597 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -24,6 +24,7 @@ package processing.app; import cc.arduino.contributions.BuiltInCoreIsNewerCheck; import cc.arduino.contributions.DownloadableContributionVersionComparator; +import cc.arduino.contributions.GPGDetachedSignatureVerifier; import cc.arduino.contributions.VersionHelper; import cc.arduino.contributions.libraries.*; import cc.arduino.contributions.libraries.ui.LibraryManagerUI; @@ -345,8 +346,8 @@ public class Base { PreferencesData.save(); if (parser.isInstallBoard()) { - ContributionsIndexer indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform()); - ContributionInstaller installer = new ContributionInstaller(indexer, BaseNoGui.getPlatform()) { + ContributionsIndexer indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier()); + ContributionInstaller installer = new ContributionInstaller(indexer, BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier()) { private String lastStatus = ""; @Override @@ -392,7 +393,7 @@ public class Base { System.exit(0); } else if (parser.isInstallLibrary()) { - LibrariesIndexer indexer = new LibrariesIndexer(BaseNoGui.getSettingsFolder(), new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform())); + LibrariesIndexer indexer = new LibrariesIndexer(BaseNoGui.getSettingsFolder(), new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier())); LibraryInstaller installer = new LibraryInstaller(indexer, BaseNoGui.getPlatform()) { private String lastStatus = ""; diff --git a/app/test/cc/arduino/packages/contributions/GPGDetachedSignatureVerifierTest.java b/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java similarity index 96% rename from app/test/cc/arduino/packages/contributions/GPGDetachedSignatureVerifierTest.java rename to app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java index c4b4c8633..7dd7285a0 100644 --- a/app/test/cc/arduino/packages/contributions/GPGDetachedSignatureVerifierTest.java +++ b/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java @@ -27,9 +27,8 @@ * the GNU General Public License. */ -package cc.arduino.packages.contributions; +package cc.arduino.contributions; -import cc.arduino.contributions.GPGDetachedSignatureVerifier; import org.junit.Before; import org.junit.Test; diff --git a/app/test/cc/arduino/packages/contributions/package_index.json b/app/test/cc/arduino/contributions/package_index.json similarity index 100% rename from app/test/cc/arduino/packages/contributions/package_index.json rename to app/test/cc/arduino/contributions/package_index.json diff --git a/app/test/cc/arduino/packages/contributions/package_index.json.sig b/app/test/cc/arduino/contributions/package_index.json.sig similarity index 100% rename from app/test/cc/arduino/packages/contributions/package_index.json.sig rename to app/test/cc/arduino/contributions/package_index.json.sig diff --git a/app/test/cc/arduino/packages/contributions/public.gpg.key b/app/test/cc/arduino/contributions/public.gpg.key similarity index 100% rename from app/test/cc/arduino/packages/contributions/public.gpg.key rename to app/test/cc/arduino/contributions/public.gpg.key diff --git a/arduino-core/src/cc/arduino/contributions/packages/Constants.java b/arduino-core/src/cc/arduino/contributions/Constants.java similarity index 94% rename from arduino-core/src/cc/arduino/contributions/packages/Constants.java rename to arduino-core/src/cc/arduino/contributions/Constants.java index 621c47f4f..c48b002c4 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/Constants.java +++ b/arduino-core/src/cc/arduino/contributions/Constants.java @@ -27,7 +27,7 @@ * the GNU General Public License. */ -package cc.arduino.contributions.packages; +package cc.arduino.contributions; import java.util.Arrays; import java.util.List; @@ -39,6 +39,7 @@ public class Constants { public static final String PACKAGE_INDEX_URL; public static final String PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS = "boardsmanager.additional.urls"; + public static final String PREF_CONTRIBUTIONS_TRUST_ALL = "contributions.trust.all"; static { String extenalPackageIndexUrl = System.getProperty("PACKAGE_INDEX_URL"); diff --git a/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java b/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java index a3ed88148..a09bd12fd 100644 --- a/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java +++ b/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java @@ -1,6 +1,8 @@ /* * This file is part of Arduino. * + * Copyright 2015 Arduino LLC (http://www.arduino.cc/) + * * Arduino is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -23,8 +25,6 @@ * the GNU General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. - * - * Copyright 2015 Arduino LLC (http://www.arduino.cc/) */ package cc.arduino.contributions; @@ -37,7 +37,7 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; import java.io.*; import java.util.Iterator; -public class GPGDetachedSignatureVerifier { +public class GPGDetachedSignatureVerifier extends SignatureVerifier { private String keyId; @@ -49,9 +49,7 @@ public class GPGDetachedSignatureVerifier { this.keyId = keyId; } - public boolean verify(File signedFile, File signature, File publicKey) throws IOException, PGPException { - PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId); - + protected boolean verify(File signedFile, File signature, File publicKey) throws IOException { FileInputStream signatureInputStream = null; FileInputStream signedFileInputStream = null; try { @@ -71,11 +69,15 @@ public class GPGDetachedSignatureVerifier { assert pgpSignatureList.size() == 1; PGPSignature pgpSignature = pgpSignatureList.get(0); + PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId); + pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey); signedFileInputStream = new FileInputStream(signedFile); pgpSignature.update(IOUtils.toByteArray(signedFileInputStream)); return pgpSignature.verify(); + } catch (PGPException e) { + throw new IOException(e); } finally { IOUtils.closeQuietly(signatureInputStream); IOUtils.closeQuietly(signedFileInputStream); diff --git a/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java new file mode 100644 index 000000000..6e2a80626 --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java @@ -0,0 +1,55 @@ +/* + * This file is part of Arduino. + * + * Copyright 2015 Arduino LLC (http://www.arduino.cc/) + * + * Arduino is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + +package cc.arduino.contributions; + +import processing.app.BaseNoGui; + +import java.io.File; +import java.io.IOException; + +public abstract class SignatureVerifier { + + public boolean isSigned(File indexFile) { + File signature = new File(indexFile.getParent(), indexFile.getName() + ".sig"); + if (!signature.exists()) { + return false; + } + + try { + return verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key")); + } catch (Exception e) { + BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e); + return false; + } + } + + protected abstract boolean verify(File signedFile, File signature, File publicKey) throws IOException; + +} diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributedPackage.java b/arduino-core/src/cc/arduino/contributions/packages/ContributedPackage.java index 3b2a91759..6720bd97c 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributedPackage.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributedPackage.java @@ -47,6 +47,8 @@ public abstract class ContributedPackage { public abstract ContributedHelp getHelp(); + private boolean trusted; + public ContributedPlatform findPlatform(String architecture, String version) { if (architecture == null || version == null) { return null; @@ -66,6 +68,14 @@ public abstract class ContributedPackage { return null; } + public boolean isTrusted() { + return trusted; + } + + public void setTrusted(boolean trusted) { + this.trusted = trusted; + } + @Override public String toString() { String res; diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributedPlatform.java b/arduino-core/src/cc/arduino/contributions/packages/ContributedPlatform.java index 0626fd851..894801fad 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributedPlatform.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributedPlatform.java @@ -54,19 +54,18 @@ public abstract class ContributedPlatform extends DownloadableContribution { public abstract ContributedHelp getHelp(); - private List resolvedTools = null; - + private List resolvedTools; private ContributedPackage parentPackage; public List getResolvedTools() { if (resolvedTools == null) { return null; } - return new LinkedList(resolvedTools); + return new LinkedList<>(resolvedTools); } public void resolveToolsDependencies(Collection packages) { - resolvedTools = new ArrayList(); + resolvedTools = new ArrayList<>(); // If there are no dependencies return empty list if (getToolsDependencies() == null) { diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java index 0de0dd562..05ad309e2 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java @@ -29,9 +29,8 @@ package cc.arduino.contributions.packages; -import cc.arduino.contributions.DownloadableContribution; -import cc.arduino.contributions.DownloadableContributionsDownloader; -import cc.arduino.contributions.GPGDetachedSignatureVerifier; +import cc.arduino.contributions.*; +import cc.arduino.contributions.Constants; import cc.arduino.filters.FileExecutablePredicate; import cc.arduino.utils.ArchiveExtractor; import cc.arduino.utils.MultiStepProgress; @@ -41,7 +40,6 @@ import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.Executor; import org.apache.commons.exec.PumpStreamHandler; -import processing.app.BaseNoGui; import processing.app.I18n; import processing.app.Platform; import processing.app.PreferencesData; @@ -52,6 +50,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.net.URL; +import java.nio.file.Files; import java.util.*; import static processing.app.I18n._; @@ -62,9 +61,11 @@ public class ContributionInstaller { private final ContributionsIndexer indexer; private final DownloadableContributionsDownloader downloader; private final Platform platform; + private final SignatureVerifier signatureVerifier; - public ContributionInstaller(ContributionsIndexer contributionsIndexer, Platform platform) { + public ContributionInstaller(ContributionsIndexer contributionsIndexer, Platform platform, SignatureVerifier signatureVerifier) { this.platform = platform; + this.signatureVerifier = signatureVerifier; File stagingFolder = contributionsIndexer.getStagingFolder(); indexer = contributionsIndexer; downloader = new DownloadableContributionsDownloader(stagingFolder) { @@ -76,13 +77,13 @@ public class ContributionInstaller { } public List install(ContributedPlatform contributedPlatform) throws Exception { - List errors = new LinkedList(); + List errors = new LinkedList<>(); if (contributedPlatform.isInstalled()) { throw new Exception("Platform is already installed!"); } // Do not download already installed tools - List tools = new LinkedList(contributedPlatform.getResolvedTools()); + List tools = new LinkedList<>(contributedPlatform.getResolvedTools()); Iterator toolsIterator = tools.iterator(); while (toolsIterator.hasNext()) { ContributedTool tool = toolsIterator.next(); @@ -134,11 +135,11 @@ public class ContributionInstaller { DownloadableContribution toolContrib = tool.getDownloadableContribution(platform); File destFolder = new File(toolsFolder, tool.getName() + File.separator + tool.getVersion()); - destFolder.mkdirs(); + Files.createDirectories(destFolder.toPath()); assert toolContrib.getDownloadedFile() != null; new ArchiveExtractor(platform).extract(toolContrib.getDownloadedFile(), destFolder, 1); try { - executePostInstallScriptIfAny(destFolder); + findAndExecutePostInstallScriptIfAny(destFolder, contributedPlatform.getParentPackage().isTrusted(), PreferencesData.getBoolean(Constants.PREF_CONTRIBUTIONS_TRUST_ALL)); } catch (IOException e) { errors.add(_("Error running post install script")); } @@ -152,10 +153,16 @@ public class ContributionInstaller { onProgress(progress); File platformFolder = new File(packageFolder, "hardware" + File.separator + contributedPlatform.getArchitecture()); File destFolder = new File(platformFolder, contributedPlatform.getParsedVersion()); - destFolder.mkdirs(); + Files.createDirectories(destFolder.toPath()); new ArchiveExtractor(platform).extract(contributedPlatform.getDownloadedFile(), destFolder, 1); contributedPlatform.setInstalled(true); contributedPlatform.setInstalledFolder(destFolder); + try { + findAndExecutePostInstallScriptIfAny(destFolder, contributedPlatform.getParentPackage().isTrusted(), PreferencesData.getBoolean(Constants.PREF_CONTRIBUTIONS_TRUST_ALL)); + } catch (IOException e) { + e.printStackTrace(); + errors.add(_("Error running post install script")); + } progress.stepDone(); progress.setStatus(_("Installation completed!")); @@ -164,20 +171,49 @@ public class ContributionInstaller { return errors; } - private void executePostInstallScriptIfAny(File folder) throws IOException { - Collection postInstallScripts = Collections2.filter(platform.postInstallScripts(folder), new FileExecutablePredicate()); + private void findAndExecutePostInstallScriptIfAny(File folder, boolean trusted, boolean trustAll) throws IOException { + Collection scripts = Collections2.filter(platform.postInstallScripts(folder), new FileExecutablePredicate()); - if (postInstallScripts.isEmpty()) { + if (scripts.isEmpty()) { String[] subfolders = folder.list(new OnlyDirs()); if (subfolders.length != 1) { return; } - executePostInstallScriptIfAny(new File(folder, subfolders[0])); + findAndExecutePostInstallScriptIfAny(new File(folder, subfolders[0]), trusted, trustAll); return; } - File postInstallScript = postInstallScripts.iterator().next(); + executeScripts(folder, scripts, trusted, trustAll); + } + + private void findAndExecutePreUninstallScriptIfAny(File folder, boolean trusted, boolean trustAll) throws IOException { + Collection scripts = Collections2.filter(platform.preUninstallScripts(folder), new FileExecutablePredicate()); + + if (scripts.isEmpty()) { + String[] subfolders = folder.list(new OnlyDirs()); + if (subfolders.length != 1) { + return; + } + + findAndExecutePreUninstallScriptIfAny(new File(folder, subfolders[0]), trusted, trustAll); + return; + } + + executeScripts(folder, scripts, trusted, trustAll); + } + + private void executeScripts(File folder, Collection postInstallScripts, boolean trusted, boolean trustAll) throws IOException { + File script = postInstallScripts.iterator().next(); + + if (!trusted && !trustAll) { + System.err.println(I18n.format(_("Warning: non trusted contribution, skipping script execution ({0})"), script)); + return; + } + + if (trustAll) { + System.err.println(I18n.format(_("Warning: forced untrusted script execution ({0})"), script)); + } ByteArrayOutputStream stdout = new ByteArrayOutputStream(); ByteArrayOutputStream stderr = new ByteArrayOutputStream(); @@ -185,7 +221,7 @@ public class ContributionInstaller { executor.setStreamHandler(new PumpStreamHandler(stdout, stderr)); executor.setWorkingDirectory(folder); executor.setExitValues(null); - int exitValue = executor.execute(new CommandLine(postInstallScript)); + int exitValue = executor.execute(new CommandLine(script)); executor.setExitValues(new int[0]); System.out.write(stdout.toByteArray()); @@ -198,9 +234,15 @@ public class ContributionInstaller { public List remove(ContributedPlatform contributedPlatform) { if (contributedPlatform == null || contributedPlatform.isReadOnly()) { - return new LinkedList(); + return new LinkedList<>(); } - List errors = new LinkedList(); + List errors = new LinkedList<>(); + try { + findAndExecutePreUninstallScriptIfAny(contributedPlatform.getInstalledFolder(), contributedPlatform.getParentPackage().isTrusted(), PreferencesData.getBoolean(Constants.PREF_CONTRIBUTIONS_TRUST_ALL)); + } catch (IOException e) { + errors.add(_("Error running post install script")); + } + FileUtils.recursiveDelete(contributedPlatform.getInstalledFolder()); contributedPlatform.setInstalled(false); contributedPlatform.setInstalledFolder(null); @@ -221,8 +263,8 @@ public class ContributionInstaller { // now try to remove the containing TOOL_NAME folder // (and silently fail if another version of the tool is installed) try { - destFolder.getParentFile().delete(); - } catch (SecurityException e) { + Files.delete(destFolder.getParentFile().toPath()); + } catch (Exception e) { // ignore } } @@ -233,11 +275,11 @@ public class ContributionInstaller { public List updateIndex() throws Exception { MultiStepProgress progress = new MultiStepProgress(1); - List downloadedPackageIndexFilesAccumulator = new LinkedList(); - downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, Constants.PACKAGE_INDEX_URL); + List downloadedPackageIndexFilesAccumulator = new LinkedList<>(); + downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, cc.arduino.contributions.Constants.PACKAGE_INDEX_URL); - Set packageIndexURLs = new HashSet(); - String additionalURLs = PreferencesData.get(Constants.PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS, ""); + Set packageIndexURLs = new HashSet<>(); + String additionalURLs = PreferencesData.get(cc.arduino.contributions.Constants.PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS, ""); if (!"".equals(additionalURLs)) { packageIndexURLs.addAll(Arrays.asList(additionalURLs.split(","))); } @@ -256,13 +298,13 @@ public class ContributionInstaller { downloadedPackagedIndexFilesAccumulator.add(packageIndex.getName()); try { File packageIndexSignature = download(progress, packageIndexUrl + ".sig"); - boolean signatureVerified = new GPGDetachedSignatureVerifier().verify(packageIndex, packageIndexSignature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key")); + boolean signatureVerified = signatureVerifier.isSigned(packageIndex); if (signatureVerified) { downloadedPackagedIndexFilesAccumulator.add(packageIndexSignature.getName()); } else { downloadedPackagedIndexFilesAccumulator.remove(packageIndex.getName()); - packageIndex.delete(); - packageIndexSignature.delete(); + Files.delete(packageIndex.toPath()); + Files.delete(packageIndexSignature.toPath()); System.err.println(I18n.format(_("{0} file signature verification failed. File ignored."), packageIndexUrl)); } } catch (Exception e) { @@ -295,15 +337,15 @@ public class ContributionInstaller { // Empty } - public void deleteUnknownFiles(List downloadedPackageIndexFiles) { + public void deleteUnknownFiles(List downloadedPackageIndexFiles) throws IOException { File preferencesFolder = indexer.getIndexFile(".").getParentFile(); - File[] additionalPackageIndexFiles = preferencesFolder.listFiles(new PackageIndexFilenameFilter(Constants.DEFAULT_INDEX_FILE_NAME)); + File[] additionalPackageIndexFiles = preferencesFolder.listFiles(new PackageIndexFilenameFilter(cc.arduino.contributions.Constants.DEFAULT_INDEX_FILE_NAME)); if (additionalPackageIndexFiles == null) { return; } for (File additionalPackageIndexFile : additionalPackageIndexFiles) { if (!downloadedPackageIndexFiles.contains(additionalPackageIndexFile.getName())) { - additionalPackageIndexFile.delete(); + Files.delete(additionalPackageIndexFile.toPath()); } } } diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndex.java b/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndex.java index 80d69b045..c72d638ec 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndex.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndex.java @@ -33,7 +33,6 @@ import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomCompar import cc.arduino.contributions.filters.DownloadableContributionWithVersionPredicate; import cc.arduino.contributions.filters.InstalledPredicate; import cc.arduino.contributions.packages.filters.PlatformArchitecturePredicate; -import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -89,7 +88,7 @@ public abstract class ContributionsIndex { } public ContributedPlatform getInstalledPlatform(String packageName, String platformArch) { - List installedPlatforms = new LinkedList(Collections2.filter(findPlatforms(packageName, platformArch), new InstalledPredicate())); + List installedPlatforms = new LinkedList<>(Collections2.filter(findPlatforms(packageName, platformArch), new InstalledPredicate())); Collections.sort(installedPlatforms, new DownloadableContributionBuiltInAtTheBottomComparator()); if (installedPlatforms.isEmpty()) { @@ -99,25 +98,11 @@ public abstract class ContributionsIndex { return installedPlatforms.get(0); } - public List getPlatforms() { - return Lists.newLinkedList(Iterables.concat(Collections2.transform(getPackages(), new Function>() { - @Override - public List apply(ContributedPackage contributedPackage) { - return contributedPackage.getPlatforms(); - } - }))); + private List getPlatforms() { + return Lists.newLinkedList(Iterables.concat(Collections2.transform(getPackages(), ContributedPackage::getPlatforms))); } - - public ContributedTool findTool(String packageName, String name, - String version) { - ContributedPackage pack = findPackage(packageName); - if (pack == null) - return null; - return pack.findTool(name, version); - } - - private final List categories = new ArrayList(); + private final List categories = new ArrayList<>(); public List getCategories() { return categories; @@ -126,10 +111,9 @@ public abstract class ContributionsIndex { public void fillCategories() { categories.clear(); for (ContributedPackage pack : getPackages()) { - for (ContributedPlatform platform : pack.getPlatforms()) { - if (!categories.contains(platform.getCategory())) - categories.add(platform.getCategory()); - } + pack.getPlatforms().stream() + .filter(platform -> !categories.contains(platform.getCategory())) + .forEach(platform -> categories.add(platform.getCategory())); } } @@ -153,4 +137,8 @@ public abstract class ContributionsIndex { res += pack + "\n"; return res; } + + public void setTrusted() { + getPackages().stream().forEach(pack -> pack.setTrusted(true)); + } } diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndexer.java b/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndexer.java index ab29cff11..8eff64829 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndexer.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndexer.java @@ -29,10 +29,7 @@ package cc.arduino.contributions.packages; -import cc.arduino.contributions.DownloadableContribution; -import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator; -import cc.arduino.contributions.GPGDetachedSignatureVerifier; -import cc.arduino.contributions.SignatureVerificationFailedException; +import cc.arduino.contributions.*; import cc.arduino.contributions.filters.BuiltInPredicate; import cc.arduino.contributions.filters.InstalledPredicate; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -46,8 +43,9 @@ import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Multimaps; import org.apache.commons.compress.utils.IOUtils; -import processing.app.BaseNoGui; +import processing.app.I18n; import processing.app.Platform; +import processing.app.PreferencesData; import processing.app.debug.TargetPackage; import processing.app.debug.TargetPlatform; import processing.app.debug.TargetPlatformException; @@ -60,6 +58,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.*; +import static processing.app.I18n._; import static processing.app.helpers.filefilters.OnlyDirs.ONLY_DIRS; public class ContributionsIndexer { @@ -68,23 +67,26 @@ public class ContributionsIndexer { private final File stagingFolder; private final File preferencesFolder; private final Platform platform; + private final SignatureVerifier signatureVerifier; private ContributionsIndex index; - public ContributionsIndexer(File preferencesFolder, Platform platform) { + public ContributionsIndexer(File preferencesFolder, Platform platform, SignatureVerifier signatureVerifier) { this.preferencesFolder = preferencesFolder; this.platform = platform; + this.signatureVerifier = signatureVerifier; packagesFolder = new File(preferencesFolder, "packages"); stagingFolder = new File(preferencesFolder, "staging" + File.separator + "packages"); } public void parseIndex() throws Exception { - File defaultIndexFile = getIndexFile(Constants.DEFAULT_INDEX_FILE_NAME); - if (!isSigned(defaultIndexFile)) { - throw new SignatureVerificationFailedException(Constants.DEFAULT_INDEX_FILE_NAME); + File defaultIndexFile = getIndexFile(cc.arduino.contributions.Constants.DEFAULT_INDEX_FILE_NAME); + if (!signatureVerifier.isSigned(defaultIndexFile)) { + throw new SignatureVerificationFailedException(cc.arduino.contributions.Constants.DEFAULT_INDEX_FILE_NAME); } index = parseIndex(defaultIndexFile); + index.setTrusted(); - File[] indexFiles = preferencesFolder.listFiles(new TestPackageIndexFilenameFilter(new PackageIndexFilenameFilter(Constants.DEFAULT_INDEX_FILE_NAME))); + File[] indexFiles = preferencesFolder.listFiles(new TestPackageIndexFilenameFilter(new PackageIndexFilenameFilter(cc.arduino.contributions.Constants.DEFAULT_INDEX_FILE_NAME))); for (File indexFile : indexFiles) { ContributionsIndex contributionsIndex = parseIndex(indexFile); @@ -113,10 +115,12 @@ public class ContributionsIndexer { } private void mergeContributions(ContributionsIndex contributionsIndex, File indexFile) { - boolean signed = isSigned(indexFile); + boolean signed = signatureVerifier.isSigned(indexFile); + boolean trustall = PreferencesData.getBoolean(Constants.PREF_CONTRIBUTIONS_TRUST_ALL); for (ContributedPackage contributedPackage : contributionsIndex.getPackages()) { - if (!signed) { + contributedPackage.setTrusted(signed || trustall); + if (!contributedPackage.isTrusted()) { for (ContributedPlatform contributedPlatform : contributedPackage.getPlatforms()) { contributedPlatform.setCategory("Contributed"); } @@ -127,7 +131,10 @@ public class ContributionsIndexer { if (targetPackage == null) { index.getPackages().add(contributedPackage); } else { - if (signed || !isPackageNameProtected(contributedPackage)) { + if (contributedPackage.isTrusted() || !isPackageNameProtected(contributedPackage)) { + if (isPackageNameProtected(contributedPackage) && trustall) { + System.err.println(I18n.format(_("Warning: forced trusting untrusted contributions"))); + } List platforms = contributedPackage.getPlatforms(); if (platforms == null) { platforms = new LinkedList(); @@ -156,21 +163,7 @@ public class ContributionsIndexer { } private boolean isPackageNameProtected(ContributedPackage contributedPackage) { - return Constants.PROTECTED_PACKAGE_NAMES.contains(contributedPackage.getName()); - } - - private boolean isSigned(File indexFile) { - File signature = new File(indexFile.getParent(), indexFile.getName() + ".sig"); - if (!signature.exists()) { - return false; - } - - try { - return new GPGDetachedSignatureVerifier().verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key")); - } catch (Exception e) { - BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e); - return false; - } + return cc.arduino.contributions.Constants.PROTECTED_PACKAGE_NAMES.contains(contributedPackage.getName()); } private ContributionsIndex parseIndex(File indexFile) throws IOException { diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index 6c0e29c31..9748f5de5 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -1,5 +1,6 @@ package processing.app; +import cc.arduino.contributions.GPGDetachedSignatureVerifier; import cc.arduino.contributions.SignatureVerificationFailedException; import cc.arduino.contributions.libraries.LibrariesIndexer; import cc.arduino.contributions.packages.ContributedTool; @@ -20,7 +21,10 @@ import processing.app.legacy.PApplet; import processing.app.packages.LibraryList; import processing.app.packages.UserLibrary; -import java.io.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -595,7 +599,7 @@ public class BaseNoGui { } static public void initPackages() throws Exception { - indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform()); + indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier()); File indexFile = indexer.getIndexFile("package_index.json"); File defaultPackageJsonFile = new File(getContentFile("dist"), "package_index.json"); if (!indexFile.isFile() || (defaultPackageJsonFile.isFile() && defaultPackageJsonFile.lastModified() > indexFile.lastModified())) { diff --git a/arduino-core/src/processing/app/Platform.java b/arduino-core/src/processing/app/Platform.java index dee2df451..9a1113172 100644 --- a/arduino-core/src/processing/app/Platform.java +++ b/arduino-core/src/processing/app/Platform.java @@ -217,12 +217,18 @@ public class Platform { } public List postInstallScripts(File folder) { - List scripts = new LinkedList(); + List scripts = new LinkedList<>(); scripts.add(new File(folder, "install_script.sh")); scripts.add(new File(folder, "post_install.sh")); return scripts; } + public List preUninstallScripts(File folder) { + List scripts = new LinkedList<>(); + scripts.add(new File(folder, "pre_uninstall.sh")); + return scripts; + } + public String getOsName() { return System.getProperty("os.name"); } diff --git a/arduino-core/src/processing/app/windows/Platform.java b/arduino-core/src/processing/app/windows/Platform.java index c4e7d394f..7dbdfd11a 100644 --- a/arduino-core/src/processing/app/windows/Platform.java +++ b/arduino-core/src/processing/app/windows/Platform.java @@ -211,11 +211,17 @@ public class Platform extends processing.app.Platform { } public List postInstallScripts(File folder) { - List scripts = new LinkedList(); + List scripts = new LinkedList<>(); scripts.add(new File(folder, "post_install.bat")); return scripts; } + public List preUninstallScripts(File folder) { + List scripts = new LinkedList<>(); + scripts.add(new File(folder, "pre_uninstall.bat")); + return scripts; + } + public void symlink(File something, File somewhere) throws IOException, InterruptedException { }