1
0
mirror of https://github.com/arduino/Arduino.git synced 2024-11-29 10:24:12 +01:00

Added core "post install" and "pre uninstall" script support.

If a core has a post/pre install/uninstall script, it will be execute at the appropriate time IF:
1) source (package_*_index) is trusted (GPG signed)
2) or users have explicitly added line "contributions.trust.all=true" to their preferences.txt
Some minor refactor and clean up while I was at it
This commit is contained in:
Federico Fissore 2015-07-03 16:00:57 +02:00
parent 7cc7d47e61
commit 987cad2633
17 changed files with 209 additions and 102 deletions

View File

@ -30,6 +30,7 @@
package cc.arduino.contributions.packages.ui; package cc.arduino.contributions.packages.ui;
import cc.arduino.contributions.DownloadableContribution; import cc.arduino.contributions.DownloadableContribution;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.contributions.packages.ContributedPlatform; import cc.arduino.contributions.packages.ContributedPlatform;
import cc.arduino.contributions.packages.ContributionInstaller; import cc.arduino.contributions.packages.ContributionInstaller;
import cc.arduino.contributions.packages.ContributionsIndexer; import cc.arduino.contributions.packages.ContributionsIndexer;
@ -116,7 +117,7 @@ public class ContributionManagerUI extends InstallerJDialog {
} }
// Create ConstributionInstaller tied with the provided index // Create ConstributionInstaller tied with the provided index
installer = new ContributionInstaller(indexer, platform) { installer = new ContributionInstaller(indexer, platform, new GPGDetachedSignatureVerifier()) {
@Override @Override
public void onProgress(Progress progress) { public void onProgress(Progress progress) {
setProgress(progress); setProgress(progress);

View File

@ -24,6 +24,7 @@ package processing.app;
import cc.arduino.contributions.BuiltInCoreIsNewerCheck; import cc.arduino.contributions.BuiltInCoreIsNewerCheck;
import cc.arduino.contributions.DownloadableContributionVersionComparator; import cc.arduino.contributions.DownloadableContributionVersionComparator;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.contributions.VersionHelper; import cc.arduino.contributions.VersionHelper;
import cc.arduino.contributions.libraries.*; import cc.arduino.contributions.libraries.*;
import cc.arduino.contributions.libraries.ui.LibraryManagerUI; import cc.arduino.contributions.libraries.ui.LibraryManagerUI;
@ -345,8 +346,8 @@ public class Base {
PreferencesData.save(); PreferencesData.save();
if (parser.isInstallBoard()) { if (parser.isInstallBoard()) {
ContributionsIndexer indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform()); ContributionsIndexer indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder(), BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier());
ContributionInstaller installer = new ContributionInstaller(indexer, BaseNoGui.getPlatform()) { ContributionInstaller installer = new ContributionInstaller(indexer, BaseNoGui.getPlatform(), new GPGDetachedSignatureVerifier()) {
private String lastStatus = ""; private String lastStatus = "";
@Override @Override
@ -392,7 +393,7 @@ public class Base {
System.exit(0); System.exit(0);
} else if (parser.isInstallLibrary()) { } 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()) { LibraryInstaller installer = new LibraryInstaller(indexer, BaseNoGui.getPlatform()) {
private String lastStatus = ""; private String lastStatus = "";

View File

@ -27,9 +27,8 @@
* the GNU General Public License. * 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.Before;
import org.junit.Test; import org.junit.Test;

View File

@ -27,7 +27,7 @@
* the GNU General Public License. * the GNU General Public License.
*/ */
package cc.arduino.contributions.packages; package cc.arduino.contributions;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -39,6 +39,7 @@ public class Constants {
public static final String PACKAGE_INDEX_URL; public static final String PACKAGE_INDEX_URL;
public static final String PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS = "boardsmanager.additional.urls"; public static final String PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS = "boardsmanager.additional.urls";
public static final String PREF_CONTRIBUTIONS_TRUST_ALL = "contributions.trust.all";
static { static {
String extenalPackageIndexUrl = System.getProperty("PACKAGE_INDEX_URL"); String extenalPackageIndexUrl = System.getProperty("PACKAGE_INDEX_URL");

View File

@ -1,6 +1,8 @@
/* /*
* This file is part of Arduino. * 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 * Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * 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 * the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by * invalidate any other reasons why the executable file might be covered by
* the GNU General Public License. * the GNU General Public License.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*/ */
package cc.arduino.contributions; package cc.arduino.contributions;
@ -37,7 +37,7 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import java.io.*; import java.io.*;
import java.util.Iterator; import java.util.Iterator;
public class GPGDetachedSignatureVerifier { public class GPGDetachedSignatureVerifier extends SignatureVerifier {
private String keyId; private String keyId;
@ -49,9 +49,7 @@ public class GPGDetachedSignatureVerifier {
this.keyId = keyId; this.keyId = keyId;
} }
public boolean verify(File signedFile, File signature, File publicKey) throws IOException, PGPException { protected boolean verify(File signedFile, File signature, File publicKey) throws IOException {
PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId);
FileInputStream signatureInputStream = null; FileInputStream signatureInputStream = null;
FileInputStream signedFileInputStream = null; FileInputStream signedFileInputStream = null;
try { try {
@ -71,11 +69,15 @@ public class GPGDetachedSignatureVerifier {
assert pgpSignatureList.size() == 1; assert pgpSignatureList.size() == 1;
PGPSignature pgpSignature = pgpSignatureList.get(0); PGPSignature pgpSignature = pgpSignatureList.get(0);
PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId);
pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey); pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey);
signedFileInputStream = new FileInputStream(signedFile); signedFileInputStream = new FileInputStream(signedFile);
pgpSignature.update(IOUtils.toByteArray(signedFileInputStream)); pgpSignature.update(IOUtils.toByteArray(signedFileInputStream));
return pgpSignature.verify(); return pgpSignature.verify();
} catch (PGPException e) {
throw new IOException(e);
} finally { } finally {
IOUtils.closeQuietly(signatureInputStream); IOUtils.closeQuietly(signatureInputStream);
IOUtils.closeQuietly(signedFileInputStream); IOUtils.closeQuietly(signedFileInputStream);

View File

@ -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;
}

View File

@ -47,6 +47,8 @@ public abstract class ContributedPackage {
public abstract ContributedHelp getHelp(); public abstract ContributedHelp getHelp();
private boolean trusted;
public ContributedPlatform findPlatform(String architecture, String version) { public ContributedPlatform findPlatform(String architecture, String version) {
if (architecture == null || version == null) { if (architecture == null || version == null) {
return null; return null;
@ -66,6 +68,14 @@ public abstract class ContributedPackage {
return null; return null;
} }
public boolean isTrusted() {
return trusted;
}
public void setTrusted(boolean trusted) {
this.trusted = trusted;
}
@Override @Override
public String toString() { public String toString() {
String res; String res;

View File

@ -54,19 +54,18 @@ public abstract class ContributedPlatform extends DownloadableContribution {
public abstract ContributedHelp getHelp(); public abstract ContributedHelp getHelp();
private List<ContributedTool> resolvedTools = null; private List<ContributedTool> resolvedTools;
private ContributedPackage parentPackage; private ContributedPackage parentPackage;
public List<ContributedTool> getResolvedTools() { public List<ContributedTool> getResolvedTools() {
if (resolvedTools == null) { if (resolvedTools == null) {
return null; return null;
} }
return new LinkedList<ContributedTool>(resolvedTools); return new LinkedList<>(resolvedTools);
} }
public void resolveToolsDependencies(Collection<ContributedPackage> packages) { public void resolveToolsDependencies(Collection<ContributedPackage> packages) {
resolvedTools = new ArrayList<ContributedTool>(); resolvedTools = new ArrayList<>();
// If there are no dependencies return empty list // If there are no dependencies return empty list
if (getToolsDependencies() == null) { if (getToolsDependencies() == null) {

View File

@ -29,9 +29,8 @@
package cc.arduino.contributions.packages; package cc.arduino.contributions.packages;
import cc.arduino.contributions.DownloadableContribution; import cc.arduino.contributions.*;
import cc.arduino.contributions.DownloadableContributionsDownloader; import cc.arduino.contributions.Constants;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.filters.FileExecutablePredicate; import cc.arduino.filters.FileExecutablePredicate;
import cc.arduino.utils.ArchiveExtractor; import cc.arduino.utils.ArchiveExtractor;
import cc.arduino.utils.MultiStepProgress; 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.DefaultExecutor;
import org.apache.commons.exec.Executor; import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler; import org.apache.commons.exec.PumpStreamHandler;
import processing.app.BaseNoGui;
import processing.app.I18n; import processing.app.I18n;
import processing.app.Platform; import processing.app.Platform;
import processing.app.PreferencesData; import processing.app.PreferencesData;
@ -52,6 +50,7 @@ import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.nio.file.Files;
import java.util.*; import java.util.*;
import static processing.app.I18n._; import static processing.app.I18n._;
@ -62,9 +61,11 @@ public class ContributionInstaller {
private final ContributionsIndexer indexer; private final ContributionsIndexer indexer;
private final DownloadableContributionsDownloader downloader; private final DownloadableContributionsDownloader downloader;
private final Platform platform; 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.platform = platform;
this.signatureVerifier = signatureVerifier;
File stagingFolder = contributionsIndexer.getStagingFolder(); File stagingFolder = contributionsIndexer.getStagingFolder();
indexer = contributionsIndexer; indexer = contributionsIndexer;
downloader = new DownloadableContributionsDownloader(stagingFolder) { downloader = new DownloadableContributionsDownloader(stagingFolder) {
@ -76,13 +77,13 @@ public class ContributionInstaller {
} }
public List<String> install(ContributedPlatform contributedPlatform) throws Exception { public List<String> install(ContributedPlatform contributedPlatform) throws Exception {
List<String> errors = new LinkedList<String>(); List<String> errors = new LinkedList<>();
if (contributedPlatform.isInstalled()) { if (contributedPlatform.isInstalled()) {
throw new Exception("Platform is already installed!"); throw new Exception("Platform is already installed!");
} }
// Do not download already installed tools // Do not download already installed tools
List<ContributedTool> tools = new LinkedList<ContributedTool>(contributedPlatform.getResolvedTools()); List<ContributedTool> tools = new LinkedList<>(contributedPlatform.getResolvedTools());
Iterator<ContributedTool> toolsIterator = tools.iterator(); Iterator<ContributedTool> toolsIterator = tools.iterator();
while (toolsIterator.hasNext()) { while (toolsIterator.hasNext()) {
ContributedTool tool = toolsIterator.next(); ContributedTool tool = toolsIterator.next();
@ -134,11 +135,11 @@ public class ContributionInstaller {
DownloadableContribution toolContrib = tool.getDownloadableContribution(platform); DownloadableContribution toolContrib = tool.getDownloadableContribution(platform);
File destFolder = new File(toolsFolder, tool.getName() + File.separator + tool.getVersion()); File destFolder = new File(toolsFolder, tool.getName() + File.separator + tool.getVersion());
destFolder.mkdirs(); Files.createDirectories(destFolder.toPath());
assert toolContrib.getDownloadedFile() != null; assert toolContrib.getDownloadedFile() != null;
new ArchiveExtractor(platform).extract(toolContrib.getDownloadedFile(), destFolder, 1); new ArchiveExtractor(platform).extract(toolContrib.getDownloadedFile(), destFolder, 1);
try { try {
executePostInstallScriptIfAny(destFolder); findAndExecutePostInstallScriptIfAny(destFolder, contributedPlatform.getParentPackage().isTrusted(), PreferencesData.getBoolean(Constants.PREF_CONTRIBUTIONS_TRUST_ALL));
} catch (IOException e) { } catch (IOException e) {
errors.add(_("Error running post install script")); errors.add(_("Error running post install script"));
} }
@ -152,10 +153,16 @@ public class ContributionInstaller {
onProgress(progress); onProgress(progress);
File platformFolder = new File(packageFolder, "hardware" + File.separator + contributedPlatform.getArchitecture()); File platformFolder = new File(packageFolder, "hardware" + File.separator + contributedPlatform.getArchitecture());
File destFolder = new File(platformFolder, contributedPlatform.getParsedVersion()); File destFolder = new File(platformFolder, contributedPlatform.getParsedVersion());
destFolder.mkdirs(); Files.createDirectories(destFolder.toPath());
new ArchiveExtractor(platform).extract(contributedPlatform.getDownloadedFile(), destFolder, 1); new ArchiveExtractor(platform).extract(contributedPlatform.getDownloadedFile(), destFolder, 1);
contributedPlatform.setInstalled(true); contributedPlatform.setInstalled(true);
contributedPlatform.setInstalledFolder(destFolder); 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.stepDone();
progress.setStatus(_("Installation completed!")); progress.setStatus(_("Installation completed!"));
@ -164,20 +171,49 @@ public class ContributionInstaller {
return errors; return errors;
} }
private void executePostInstallScriptIfAny(File folder) throws IOException { private void findAndExecutePostInstallScriptIfAny(File folder, boolean trusted, boolean trustAll) throws IOException {
Collection<File> postInstallScripts = Collections2.filter(platform.postInstallScripts(folder), new FileExecutablePredicate()); Collection<File> scripts = Collections2.filter(platform.postInstallScripts(folder), new FileExecutablePredicate());
if (postInstallScripts.isEmpty()) { if (scripts.isEmpty()) {
String[] subfolders = folder.list(new OnlyDirs()); String[] subfolders = folder.list(new OnlyDirs());
if (subfolders.length != 1) { if (subfolders.length != 1) {
return; return;
} }
executePostInstallScriptIfAny(new File(folder, subfolders[0])); findAndExecutePostInstallScriptIfAny(new File(folder, subfolders[0]), trusted, trustAll);
return; return;
} }
File postInstallScript = postInstallScripts.iterator().next(); executeScripts(folder, scripts, trusted, trustAll);
}
private void findAndExecutePreUninstallScriptIfAny(File folder, boolean trusted, boolean trustAll) throws IOException {
Collection<File> 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<File> 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 stdout = new ByteArrayOutputStream();
ByteArrayOutputStream stderr = new ByteArrayOutputStream(); ByteArrayOutputStream stderr = new ByteArrayOutputStream();
@ -185,7 +221,7 @@ public class ContributionInstaller {
executor.setStreamHandler(new PumpStreamHandler(stdout, stderr)); executor.setStreamHandler(new PumpStreamHandler(stdout, stderr));
executor.setWorkingDirectory(folder); executor.setWorkingDirectory(folder);
executor.setExitValues(null); executor.setExitValues(null);
int exitValue = executor.execute(new CommandLine(postInstallScript)); int exitValue = executor.execute(new CommandLine(script));
executor.setExitValues(new int[0]); executor.setExitValues(new int[0]);
System.out.write(stdout.toByteArray()); System.out.write(stdout.toByteArray());
@ -198,9 +234,15 @@ public class ContributionInstaller {
public List<String> remove(ContributedPlatform contributedPlatform) { public List<String> remove(ContributedPlatform contributedPlatform) {
if (contributedPlatform == null || contributedPlatform.isReadOnly()) { if (contributedPlatform == null || contributedPlatform.isReadOnly()) {
return new LinkedList<String>(); return new LinkedList<>();
} }
List<String> errors = new LinkedList<String>(); List<String> 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()); FileUtils.recursiveDelete(contributedPlatform.getInstalledFolder());
contributedPlatform.setInstalled(false); contributedPlatform.setInstalled(false);
contributedPlatform.setInstalledFolder(null); contributedPlatform.setInstalledFolder(null);
@ -221,8 +263,8 @@ public class ContributionInstaller {
// now try to remove the containing TOOL_NAME folder // now try to remove the containing TOOL_NAME folder
// (and silently fail if another version of the tool is installed) // (and silently fail if another version of the tool is installed)
try { try {
destFolder.getParentFile().delete(); Files.delete(destFolder.getParentFile().toPath());
} catch (SecurityException e) { } catch (Exception e) {
// ignore // ignore
} }
} }
@ -233,11 +275,11 @@ public class ContributionInstaller {
public List<String> updateIndex() throws Exception { public List<String> updateIndex() throws Exception {
MultiStepProgress progress = new MultiStepProgress(1); MultiStepProgress progress = new MultiStepProgress(1);
List<String> downloadedPackageIndexFilesAccumulator = new LinkedList<String>(); List<String> downloadedPackageIndexFilesAccumulator = new LinkedList<>();
downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, Constants.PACKAGE_INDEX_URL); downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, cc.arduino.contributions.Constants.PACKAGE_INDEX_URL);
Set<String> packageIndexURLs = new HashSet<String>(); Set<String> packageIndexURLs = new HashSet<>();
String additionalURLs = PreferencesData.get(Constants.PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS, ""); String additionalURLs = PreferencesData.get(cc.arduino.contributions.Constants.PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS, "");
if (!"".equals(additionalURLs)) { if (!"".equals(additionalURLs)) {
packageIndexURLs.addAll(Arrays.asList(additionalURLs.split(","))); packageIndexURLs.addAll(Arrays.asList(additionalURLs.split(",")));
} }
@ -256,13 +298,13 @@ public class ContributionInstaller {
downloadedPackagedIndexFilesAccumulator.add(packageIndex.getName()); downloadedPackagedIndexFilesAccumulator.add(packageIndex.getName());
try { try {
File packageIndexSignature = download(progress, packageIndexUrl + ".sig"); 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) { if (signatureVerified) {
downloadedPackagedIndexFilesAccumulator.add(packageIndexSignature.getName()); downloadedPackagedIndexFilesAccumulator.add(packageIndexSignature.getName());
} else { } else {
downloadedPackagedIndexFilesAccumulator.remove(packageIndex.getName()); downloadedPackagedIndexFilesAccumulator.remove(packageIndex.getName());
packageIndex.delete(); Files.delete(packageIndex.toPath());
packageIndexSignature.delete(); Files.delete(packageIndexSignature.toPath());
System.err.println(I18n.format(_("{0} file signature verification failed. File ignored."), packageIndexUrl)); System.err.println(I18n.format(_("{0} file signature verification failed. File ignored."), packageIndexUrl));
} }
} catch (Exception e) { } catch (Exception e) {
@ -295,15 +337,15 @@ public class ContributionInstaller {
// Empty // Empty
} }
public void deleteUnknownFiles(List<String> downloadedPackageIndexFiles) { public void deleteUnknownFiles(List<String> downloadedPackageIndexFiles) throws IOException {
File preferencesFolder = indexer.getIndexFile(".").getParentFile(); 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) { if (additionalPackageIndexFiles == null) {
return; return;
} }
for (File additionalPackageIndexFile : additionalPackageIndexFiles) { for (File additionalPackageIndexFile : additionalPackageIndexFiles) {
if (!downloadedPackageIndexFiles.contains(additionalPackageIndexFile.getName())) { if (!downloadedPackageIndexFiles.contains(additionalPackageIndexFile.getName())) {
additionalPackageIndexFile.delete(); Files.delete(additionalPackageIndexFile.toPath());
} }
} }
} }

View File

@ -33,7 +33,6 @@ import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomCompar
import cc.arduino.contributions.filters.DownloadableContributionWithVersionPredicate; import cc.arduino.contributions.filters.DownloadableContributionWithVersionPredicate;
import cc.arduino.contributions.filters.InstalledPredicate; import cc.arduino.contributions.filters.InstalledPredicate;
import cc.arduino.contributions.packages.filters.PlatformArchitecturePredicate; import cc.arduino.contributions.packages.filters.PlatformArchitecturePredicate;
import com.google.common.base.Function;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@ -89,7 +88,7 @@ public abstract class ContributionsIndex {
} }
public ContributedPlatform getInstalledPlatform(String packageName, String platformArch) { public ContributedPlatform getInstalledPlatform(String packageName, String platformArch) {
List<ContributedPlatform> installedPlatforms = new LinkedList<ContributedPlatform>(Collections2.filter(findPlatforms(packageName, platformArch), new InstalledPredicate())); List<ContributedPlatform> installedPlatforms = new LinkedList<>(Collections2.filter(findPlatforms(packageName, platformArch), new InstalledPredicate()));
Collections.sort(installedPlatforms, new DownloadableContributionBuiltInAtTheBottomComparator()); Collections.sort(installedPlatforms, new DownloadableContributionBuiltInAtTheBottomComparator());
if (installedPlatforms.isEmpty()) { if (installedPlatforms.isEmpty()) {
@ -99,25 +98,11 @@ public abstract class ContributionsIndex {
return installedPlatforms.get(0); return installedPlatforms.get(0);
} }
public List<ContributedPlatform> getPlatforms() { private List<ContributedPlatform> getPlatforms() {
return Lists.newLinkedList(Iterables.concat(Collections2.transform(getPackages(), new Function<ContributedPackage, List<ContributedPlatform>>() { return Lists.newLinkedList(Iterables.concat(Collections2.transform(getPackages(), ContributedPackage::getPlatforms)));
@Override
public List<ContributedPlatform> apply(ContributedPackage contributedPackage) {
return contributedPackage.getPlatforms();
}
})));
} }
private final List<String> categories = new ArrayList<>();
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<String> categories = new ArrayList<String>();
public List<String> getCategories() { public List<String> getCategories() {
return categories; return categories;
@ -126,10 +111,9 @@ public abstract class ContributionsIndex {
public void fillCategories() { public void fillCategories() {
categories.clear(); categories.clear();
for (ContributedPackage pack : getPackages()) { for (ContributedPackage pack : getPackages()) {
for (ContributedPlatform platform : pack.getPlatforms()) { pack.getPlatforms().stream()
if (!categories.contains(platform.getCategory())) .filter(platform -> !categories.contains(platform.getCategory()))
categories.add(platform.getCategory()); .forEach(platform -> categories.add(platform.getCategory()));
}
} }
} }
@ -153,4 +137,8 @@ public abstract class ContributionsIndex {
res += pack + "\n"; res += pack + "\n";
return res; return res;
} }
public void setTrusted() {
getPackages().stream().forEach(pack -> pack.setTrusted(true));
}
} }

View File

@ -29,10 +29,7 @@
package cc.arduino.contributions.packages; package cc.arduino.contributions.packages;
import cc.arduino.contributions.DownloadableContribution; import cc.arduino.contributions.*;
import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.contributions.SignatureVerificationFailedException;
import cc.arduino.contributions.filters.BuiltInPredicate; import cc.arduino.contributions.filters.BuiltInPredicate;
import cc.arduino.contributions.filters.InstalledPredicate; import cc.arduino.contributions.filters.InstalledPredicate;
import com.fasterxml.jackson.databind.DeserializationFeature; 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.Iterables;
import com.google.common.collect.Multimaps; import com.google.common.collect.Multimaps;
import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.compress.utils.IOUtils;
import processing.app.BaseNoGui; import processing.app.I18n;
import processing.app.Platform; import processing.app.Platform;
import processing.app.PreferencesData;
import processing.app.debug.TargetPackage; import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform; import processing.app.debug.TargetPlatform;
import processing.app.debug.TargetPlatformException; import processing.app.debug.TargetPlatformException;
@ -60,6 +58,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.*; import java.util.*;
import static processing.app.I18n._;
import static processing.app.helpers.filefilters.OnlyDirs.ONLY_DIRS; import static processing.app.helpers.filefilters.OnlyDirs.ONLY_DIRS;
public class ContributionsIndexer { public class ContributionsIndexer {
@ -68,23 +67,26 @@ public class ContributionsIndexer {
private final File stagingFolder; private final File stagingFolder;
private final File preferencesFolder; private final File preferencesFolder;
private final Platform platform; private final Platform platform;
private final SignatureVerifier signatureVerifier;
private ContributionsIndex index; private ContributionsIndex index;
public ContributionsIndexer(File preferencesFolder, Platform platform) { public ContributionsIndexer(File preferencesFolder, Platform platform, SignatureVerifier signatureVerifier) {
this.preferencesFolder = preferencesFolder; this.preferencesFolder = preferencesFolder;
this.platform = platform; this.platform = platform;
this.signatureVerifier = signatureVerifier;
packagesFolder = new File(preferencesFolder, "packages"); packagesFolder = new File(preferencesFolder, "packages");
stagingFolder = new File(preferencesFolder, "staging" + File.separator + "packages"); stagingFolder = new File(preferencesFolder, "staging" + File.separator + "packages");
} }
public void parseIndex() throws Exception { public void parseIndex() throws Exception {
File defaultIndexFile = getIndexFile(Constants.DEFAULT_INDEX_FILE_NAME); File defaultIndexFile = getIndexFile(cc.arduino.contributions.Constants.DEFAULT_INDEX_FILE_NAME);
if (!isSigned(defaultIndexFile)) { if (!signatureVerifier.isSigned(defaultIndexFile)) {
throw new SignatureVerificationFailedException(Constants.DEFAULT_INDEX_FILE_NAME); throw new SignatureVerificationFailedException(cc.arduino.contributions.Constants.DEFAULT_INDEX_FILE_NAME);
} }
index = parseIndex(defaultIndexFile); 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) { for (File indexFile : indexFiles) {
ContributionsIndex contributionsIndex = parseIndex(indexFile); ContributionsIndex contributionsIndex = parseIndex(indexFile);
@ -113,10 +115,12 @@ public class ContributionsIndexer {
} }
private void mergeContributions(ContributionsIndex contributionsIndex, File indexFile) { 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()) { for (ContributedPackage contributedPackage : contributionsIndex.getPackages()) {
if (!signed) { contributedPackage.setTrusted(signed || trustall);
if (!contributedPackage.isTrusted()) {
for (ContributedPlatform contributedPlatform : contributedPackage.getPlatforms()) { for (ContributedPlatform contributedPlatform : contributedPackage.getPlatforms()) {
contributedPlatform.setCategory("Contributed"); contributedPlatform.setCategory("Contributed");
} }
@ -127,7 +131,10 @@ public class ContributionsIndexer {
if (targetPackage == null) { if (targetPackage == null) {
index.getPackages().add(contributedPackage); index.getPackages().add(contributedPackage);
} else { } 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<ContributedPlatform> platforms = contributedPackage.getPlatforms(); List<ContributedPlatform> platforms = contributedPackage.getPlatforms();
if (platforms == null) { if (platforms == null) {
platforms = new LinkedList<ContributedPlatform>(); platforms = new LinkedList<ContributedPlatform>();
@ -156,21 +163,7 @@ public class ContributionsIndexer {
} }
private boolean isPackageNameProtected(ContributedPackage contributedPackage) { private boolean isPackageNameProtected(ContributedPackage contributedPackage) {
return Constants.PROTECTED_PACKAGE_NAMES.contains(contributedPackage.getName()); return cc.arduino.contributions.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;
}
} }
private ContributionsIndex parseIndex(File indexFile) throws IOException { private ContributionsIndex parseIndex(File indexFile) throws IOException {

View File

@ -1,5 +1,6 @@
package processing.app; package processing.app;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.contributions.SignatureVerificationFailedException; import cc.arduino.contributions.SignatureVerificationFailedException;
import cc.arduino.contributions.libraries.LibrariesIndexer; import cc.arduino.contributions.libraries.LibrariesIndexer;
import cc.arduino.contributions.packages.ContributedTool; import cc.arduino.contributions.packages.ContributedTool;
@ -20,7 +21,10 @@ import processing.app.legacy.PApplet;
import processing.app.packages.LibraryList; import processing.app.packages.LibraryList;
import processing.app.packages.UserLibrary; 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.*;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -595,7 +599,7 @@ public class BaseNoGui {
} }
static public void initPackages() throws Exception { 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 indexFile = indexer.getIndexFile("package_index.json");
File defaultPackageJsonFile = new File(getContentFile("dist"), "package_index.json"); File defaultPackageJsonFile = new File(getContentFile("dist"), "package_index.json");
if (!indexFile.isFile() || (defaultPackageJsonFile.isFile() && defaultPackageJsonFile.lastModified() > indexFile.lastModified())) { if (!indexFile.isFile() || (defaultPackageJsonFile.isFile() && defaultPackageJsonFile.lastModified() > indexFile.lastModified())) {

View File

@ -217,12 +217,18 @@ public class Platform {
} }
public List<File> postInstallScripts(File folder) { public List<File> postInstallScripts(File folder) {
List<File> scripts = new LinkedList<File>(); List<File> scripts = new LinkedList<>();
scripts.add(new File(folder, "install_script.sh")); scripts.add(new File(folder, "install_script.sh"));
scripts.add(new File(folder, "post_install.sh")); scripts.add(new File(folder, "post_install.sh"));
return scripts; return scripts;
} }
public List<File> preUninstallScripts(File folder) {
List<File> scripts = new LinkedList<>();
scripts.add(new File(folder, "pre_uninstall.sh"));
return scripts;
}
public String getOsName() { public String getOsName() {
return System.getProperty("os.name"); return System.getProperty("os.name");
} }

View File

@ -211,11 +211,17 @@ public class Platform extends processing.app.Platform {
} }
public List<File> postInstallScripts(File folder) { public List<File> postInstallScripts(File folder) {
List<File> scripts = new LinkedList<File>(); List<File> scripts = new LinkedList<>();
scripts.add(new File(folder, "post_install.bat")); scripts.add(new File(folder, "post_install.bat"));
return scripts; return scripts;
} }
public List<File> preUninstallScripts(File folder) {
List<File> scripts = new LinkedList<>();
scripts.add(new File(folder, "pre_uninstall.bat"));
return scripts;
}
public void symlink(File something, File somewhere) throws IOException, InterruptedException { public void symlink(File something, File somewhere) throws IOException, InterruptedException {
} }