diff options
205 files changed, 5045 insertions, 1515 deletions
diff --git a/Jenkinsfile b/Jenkinsfile index 180fdac75..4f0e0ff28 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -49,7 +49,7 @@ pipeline { } } steps { - sh "wget https://git.eclipse.org/c/platform/eclipse.platform.releng.aggregator.git/plain/scripts/verifyFreezePeriod.sh" + sh "wget https://raw.githubusercontent.com/eclipse-platform/eclipse.platform.releng.aggregator/master/scripts/verifyFreezePeriod.sh" sh "chmod +x verifyFreezePeriod.sh" withCredentials([string(credentialsId: 'google-api-key', variable: 'GOOGLE_API_KEY')]) { sh './verifyFreezePeriod.sh' diff --git a/bundles/org.eclipse.equinox.frameworkadmin.equinox/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.frameworkadmin.equinox/META-INF/MANIFEST.MF index 655b1355f..5ad6e58c5 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.equinox/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.frameworkadmin.equinox/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.frameworkadmin.equinox;singleton:=true -Bundle-Version: 1.2.100.qualifier +Bundle-Version: 1.2.200.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Import-Package: org.eclipse.equinox.frameworkadmin;version="[2.0.0,3.0.0)", @@ -20,7 +20,7 @@ Import-Package: org.eclipse.equinox.frameworkadmin;version="[2.0.0,3.0.0)", org.osgi.util.tracker;version="1.3.0" Export-Package: org.eclipse.equinox.internal.frameworkadmin.equinox;x-friends:="org.eclipse.equinox.p2.publisher,org.eclipse.equinox.p2.publisher.eclipse,org.eclipse.equinox.simpleconfigurator.manipulator", org.eclipse.equinox.internal.frameworkadmin.equinox.utils;x-friends:="org.eclipse.equinox.p2.publisher.eclipse" -Require-Bundle: org.eclipse.equinox.common +Require-Bundle: org.eclipse.equinox.common;bundle-version="3.16.000" Bundle-RequiredExecutionEnvironment: JavaSE-11 Service-Component: OSGI-INF/fwadmin.xml Bundle-ActivationPolicy: lazy diff --git a/bundles/org.eclipse.equinox.frameworkadmin.equinox/pom.xml b/bundles/org.eclipse.equinox.frameworkadmin.equinox/pom.xml index 33bf8e699..c3ecc7ec9 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.equinox/pom.xml +++ b/bundles/org.eclipse.equinox.frameworkadmin.equinox/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.frameworkadmin.equinox</artifactId> - <version>1.2.100-SNAPSHOT</version> + <version>1.2.200-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxManipulatorImpl.java b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxManipulatorImpl.java index 5b73611ae..8e16a04c8 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxManipulatorImpl.java +++ b/bundles/org.eclipse.equinox.frameworkadmin.equinox/src/org/eclipse/equinox/internal/frameworkadmin/equinox/EquinoxManipulatorImpl.java @@ -328,20 +328,19 @@ public class EquinoxManipulatorImpl implements Manipulator { BundleInfo[] bInfos = new BundleInfo[bundles.length]; for (int i = 0; i < bundles.length; i++) { // System.out.println("bundles[" + i + "]=" + bundles[i]); - try { + Optional<File> bundleFile = FileLocator.getBundleFileLocation(bundles[i]); + if (bundleFile.isPresent()) { if (bundles[i].getBundleId() == 0) // SystemBundle bInfos[i] = new BundleInfo(bundles[i].getSymbolicName(), bundles[i].getHeaders("").get(Constants.BUNDLE_VERSION), //$NON-NLS-1$ - FileLocator.getBundleFile(bundles[i]).getAbsoluteFile().toURI(), -1, true); + bundleFile.get().getAbsoluteFile().toURI(), -1, true); else { bInfos[i] = new BundleInfo(bundles[i].getSymbolicName(), bundles[i].getHeaders("").get(Constants.BUNDLE_VERSION), //$NON-NLS-1$ - FileLocator.getBundleFile(bundles[i]).getAbsoluteFile().toURI(), + bundleFile.get().getAbsoluteFile().toURI(), bundles[i].adapt(BundleStartLevel.class).getStartLevel(), bundles[i].adapt(BundleStartLevel.class).isPersistentlyStarted()); } - } catch (IOException e) { - e.printStackTrace(); } } configData.setBundles(bInfos); diff --git a/bundles/org.eclipse.equinox.frameworkadmin.test/pom.xml b/bundles/org.eclipse.equinox.frameworkadmin.test/pom.xml index a717a575d..d48775cd2 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin.test/pom.xml +++ b/bundles/org.eclipse.equinox.frameworkadmin.test/pom.xml @@ -4,7 +4,7 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.frameworkadmin/pom.xml b/bundles/org.eclipse.equinox.frameworkadmin/pom.xml index aedce9f7a..433dcf50f 100644 --- a/bundles/org.eclipse.equinox.frameworkadmin/pom.xml +++ b/bundles/org.eclipse.equinox.frameworkadmin/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml index 8e091d594..72d6db126 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml +++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.optimizers/pom.xml b/bundles/org.eclipse.equinox.p2.artifact.optimizers/pom.xml index ca540db6b..8d09d7881 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.optimizers/pom.xml +++ b/bundles/org.eclipse.equinox.p2.artifact.optimizers/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.processors/pom.xml b/bundles/org.eclipse.equinox.p2.artifact.processors/pom.xml index 1d3ab96a1..08e7da3c8 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.processors/pom.xml +++ b/bundles/org.eclipse.equinox.p2.artifact.processors/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF index 49e639b77..05dc70a3c 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF @@ -10,7 +10,7 @@ Export-Package: org.eclipse.equinox.internal.p2.artifact.processing;x-friends:=" org.eclipse.equinox.internal.p2.artifact.processors.checksum;x-friends:="org.eclipse.equinox.p2.publisher", org.eclipse.equinox.internal.p2.artifact.processors.md5;x-internal:=true, org.eclipse.equinox.internal.p2.artifact.processors.pack200;x-friends:="org.eclipse.equinox.p2.artifact.processors,org.eclipse.equinox.p2.artifact.optimizers", - org.eclipse.equinox.internal.p2.artifact.processors.pgp;x-friends:="org.eclipse.equinox.p2.engine,org.eclipse.equinox.p2.ui.sdk", + org.eclipse.equinox.internal.p2.artifact.processors.pgp;x-friends:="org.eclipse.equinox.p2.engine,org.eclipse.equinox.p2.ui.sdk,org.eclipse.equinox.p2.ui", org.eclipse.equinox.internal.p2.artifact.repository; x-friends:="org.eclipse.equinox.p2.publisher, org.eclipse.equinox.p2.reconciler.dropins, diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/pom.xml b/bundles/org.eclipse.equinox.p2.artifact.repository/pom.xml index 896b53571..bc016ef5a 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/pom.xml +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/Messages.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/Messages.java index 4ae7cc402..4f522f0a0 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/Messages.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/Messages.java @@ -15,11 +15,15 @@ import org.eclipse.osgi.util.NLS; public class Messages extends NLS { private static final String BUNDLE_NAME = "org.eclipse.equinox.internal.p2.artifact.processors.pgp.messages"; //$NON-NLS-1$ + public static String Error_SignatureAfterKeyExpiration; + + public static String Error_SignatureAfterKeyRevocation; + public static String Error_SignatureAndFileDontMatch; public static String Error_CouldNotLoadSignature; - public static String Error_publicKeyNotFound; + public static String Warning_publicKeyNotFound; static { // initialize resource bundle diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java index 688765ff7..8f3d15083 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java @@ -14,6 +14,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.*; import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; @@ -21,20 +22,21 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.equinox.internal.p2.artifact.repository.Activator; import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; public class PGPPublicKeyStore { - private Map<Long, PGPPublicKey> keys = new HashMap<>(); + private Map<String, PGPPublicKey> keys = new LinkedHashMap<>(); public PGPPublicKey addKey(PGPPublicKey key) { if (key == null) { return null; } - PGPPublicKey alreadyStoredKey = keys.putIfAbsent(key.getKeyID(), key); + PGPPublicKey alreadyStoredKey = keys.putIfAbsent(PGPPublicKeyService.toHexFingerprint(key), key); return alreadyStoredKey == null ? key : alreadyStoredKey; } - public PGPPublicKey getKey(long id) { - return keys.get(id); + public Collection<PGPPublicKey> getKeys(long id) { + return keys.values().stream().filter(key -> key.getKeyID() == id).collect(Collectors.toList()); } public void addKeys(String... armoredPublicKeys) { @@ -63,9 +65,7 @@ public class PGPPublicKeyStore { public String toArmoredString() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); ArmoredOutputStream armoredOut = new ArmoredOutputStream(out); - // PGPPublicKeyRing ring = new PGPPublicKeyRing(new ArrayList<>(keys.values())); - // ring.encode(armoredOut); - for (PGPPublicKey key : keys.values()) { + for (PGPPublicKey key : all()) { key.encode(armoredOut); } armoredOut.close(); @@ -74,14 +74,14 @@ public class PGPPublicKeyStore { } public void remove(PGPPublicKey selectedKey) { - keys.remove(selectedKey.getKeyID()); + keys.remove(PGPPublicKeyService.toHexFingerprint(selectedKey)); } public void add(File file) { try (InputStream stream = new FileInputStream(file)) { readPublicKeys(stream).forEach(this::addKey); } catch (IOException e) { - // TODO: how to log? + LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "Could not read PGP key from " + file, e)); //$NON-NLS-1$ } } @@ -96,7 +96,8 @@ public class PGPPublicKeyStore { } Set<PGPPublicKey> res = new HashSet<>(); try (InputStream stream = PGPUtil.getDecoderStream(new ByteArrayInputStream( - PGPSignatureVerifier.unnormalizedPGPProperty(armoredPublicKeyring).getBytes()))) { + PGPSignatureVerifier.unnormalizedPGPProperty(armoredPublicKeyring) + .getBytes(StandardCharsets.US_ASCII)))) { new JcaPGPObjectFactory(stream).forEach(o -> { if (o instanceof PGPPublicKeyRingCollection) { collectKeys((PGPPublicKeyRingCollection) o, res::add); @@ -108,7 +109,7 @@ public class PGPPublicKeyStore { res.add((PGPPublicKey) o); } }); - } catch (IOException e) { + } catch (IOException | PGPRuntimeOperationException e) { LogHelper.log(new Status(IStatus.ERROR, Activator.ID, e.getMessage(), e)); } return res; diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java index d752d1b61..b994b7740 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java @@ -11,22 +11,30 @@ package org.eclipse.equinox.internal.p2.artifact.processors.pgp; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.Map.Entry; import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.openpgp.*; import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; +import org.bouncycastle.openpgp.operator.PGPContentVerifier; +import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilder; import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; import org.eclipse.core.runtime.*; import org.eclipse.equinox.internal.p2.artifact.repository.Activator; +import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStep; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.repository.artifact.*; +import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.osgi.util.NLS; /** - * This processing step verifies PGP signatures are correct (ie artifact was not - * tampered during fetch). Note that is does <b>not</b> deal with trust. Dealing - * with trusted signers is done as part of CheckTrust touchpoint and phase. + * This processing step verifies PGP signatures are correct (i.e., the artifact + * was not tampered during fetch). Note that is does <b>not</b> deal with trust. + * Dealing with trusted signers is done as part of CheckTrust and touchpoint + * phase. */ public final class PGPSignatureVerifier extends ProcessingStep { @@ -37,11 +45,19 @@ public final class PGPSignatureVerifier extends ProcessingStep { */ public static final String ID = "org.eclipse.equinox.p2.processing.PGPSignatureCheck"; //$NON-NLS-1$ - public static final PGPPublicKeyStore KNOWN_KEYS = new PGPPublicKeyStore(); - public static final String PGP_SIGNER_KEYS_PROPERTY_NAME = "pgp.publicKeys"; //$NON-NLS-1$ + public static final String PGP_SIGNATURES_PROPERTY_NAME = "pgp.signatures"; //$NON-NLS-1$ - private Collection<PGPSignature> signaturesToVerify; + + private PGPPublicKeyService keyService; + + private IArtifactDescriptor sourceDescriptor; + + private Map<PGPSignature, List<PGPContentVerifier>> signaturesToVerify = new LinkedHashMap<>(); + + private Map<PGPContentVerifier, PGPPublicKey> verifierKeys = new LinkedHashMap<>(); + + private List<OutputStream> signatureVerifiers = new ArrayList<>(); public PGPSignatureVerifier() { super(); @@ -55,7 +71,8 @@ public final class PGPSignatureVerifier extends ProcessingStep { return Collections.emptyList(); } List<PGPSignature> res = new ArrayList<>(); - try (InputStream in = new ArmoredInputStream(new ByteArrayInputStream(signatureText.getBytes()))) { + try (InputStream in = new ArmoredInputStream( + new ByteArrayInputStream(signatureText.getBytes(StandardCharsets.US_ASCII)))) { PGPObjectFactory pgpFactory = new BcPGPObjectFactory(in); Object o = pgpFactory.nextObject(); PGPSignatureList signatureList = new PGPSignatureList(new PGPSignature[0]); @@ -71,43 +88,74 @@ public final class PGPSignatureVerifier extends ProcessingStep { return res; } + public static PGPPublicKeyStore getKeys(IArtifactDescriptor artifact) { + PGPPublicKeyStore keyStore = new PGPPublicKeyStore(); + String keyText = artifact.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME); + PGPPublicKeyStore.readPublicKeys(keyText).stream().forEach(keyStore::addKey); + return keyStore; + } + @Override public void initialize(IProvisioningAgent agent, IProcessingStepDescriptor descriptor, IArtifactDescriptor context) { super.initialize(agent, descriptor, context); + + sourceDescriptor = context; + keyService = agent.getService(PGPPublicKeyService.class); + // 1. verify declared public keys have signature from a trusted key, if so, add to KeyStore -// 2. verify artifact signature matches signture of given keys, and at least 1 of this key is trusted +// 2. verify artifact signature matches signature of given keys, and at least 1 of this key is trusted String signatureText = unnormalizedPGPProperty(context.getProperty(PGP_SIGNATURES_PROPERTY_NAME)); if (signatureText == null) { setStatus(Status.OK_STATUS); return; } + + Collection<PGPSignature> signatures; try { - signaturesToVerify = getSignatures(context); + signatures = getSignatures(context); } catch (Exception ex) { setStatus(new Status(IStatus.ERROR, Activator.ID, Messages.Error_CouldNotLoadSignature, ex)); return; } - if (signaturesToVerify.isEmpty()) { + + if (signatures.isEmpty()) { setStatus(Status.OK_STATUS); return; } IArtifactRepository repository = context.getRepository(); - KNOWN_KEYS.addKeys(context.getProperty(PGP_SIGNER_KEYS_PROPERTY_NAME), - repository != null ? repository.getProperty(PGP_SIGNER_KEYS_PROPERTY_NAME) : null); - for (PGPSignature signature : signaturesToVerify) { - PGPPublicKey publicKey = KNOWN_KEYS.getKey(signature.getKeyID()); - if (publicKey == null) { - setStatus(new Status(IStatus.ERROR, Activator.ID, - NLS.bind(Messages.Error_publicKeyNotFound, Long.toHexString(signature.getKeyID())))); - return; - } - try { - signature.init(new BcPGPContentVerifierBuilderProvider(), publicKey); - } catch (PGPException ex) { - setStatus(new Status(IStatus.ERROR, Activator.ID, ex.getMessage(), ex)); - return; + + PGPPublicKeyStore.readPublicKeys(context.getProperty(PGP_SIGNER_KEYS_PROPERTY_NAME)) + .forEach(keyService::addKey); + if (repository != null) { + PGPPublicKeyStore.readPublicKeys(repository.getProperty(PGP_SIGNER_KEYS_PROPERTY_NAME)) + .forEach(keyService::addKey); + } + + for (PGPSignature signature : signatures) { + long keyID = signature.getKeyID(); + Collection<PGPPublicKey> keys = keyService.getKeys(keyID); + if (keys.isEmpty()) { + LogHelper.log(new Status(IStatus.WARNING, Activator.ID, + NLS.bind(Messages.Warning_publicKeyNotFound, PGPPublicKeyService.toHex(keyID), + context.getArtifactKey().getId()))); + } else { + try { + PGPContentVerifierBuilder verifierBuilder = new BcPGPContentVerifierBuilderProvider() + .get(signature.getKeyAlgorithm(), signature.getHashAlgorithm()); + List<PGPContentVerifier> verifiers = new ArrayList<>(); + signaturesToVerify.put(signature, verifiers); + for (PGPPublicKey key : keys) { + PGPContentVerifier verifier = verifierBuilder.build(key); + verifierKeys.put(verifier, key); + verifiers.add(verifier); + signatureVerifiers.add(verifier.getOutputStream()); + } + } catch (PGPException ex) { + setStatus(new Status(IStatus.ERROR, Activator.ID, ex.getMessage(), ex)); + return; + } } } } @@ -134,51 +182,94 @@ public final class PGPSignatureVerifier extends ProcessingStep { } @Override - public void write(int b) { - if (signaturesToVerify != null) { - signaturesToVerify.iterator().forEachRemaining(signature -> signature.update((byte) b)); - } - - } - - @Override - public void write(byte[] b) throws IOException { + public void write(int b) throws IOException { getDestination().write(b); - if (signaturesToVerify != null) { - signaturesToVerify.iterator().forEachRemaining(signature -> signature.update(b)); + for (OutputStream verifier : signatureVerifiers) { + verifier.write(b); } } @Override public void write(byte[] b, int off, int len) throws IOException { getDestination().write(b, off, len); - if (signaturesToVerify != null) { - signaturesToVerify.iterator().forEachRemaining(signature -> signature.update(b, off, len)); + for (OutputStream verifier : signatureVerifiers) { + verifier.write(b, off, len); } } @Override - public void close() { - if (!getStatus().isOK()) { - return; - } - if (signaturesToVerify == null || signaturesToVerify.isEmpty()) { - return; - } - Iterator<PGPSignature> iterator = signaturesToVerify.iterator(); - while (iterator.hasNext()) { - PGPSignature signature = iterator.next(); - try { - if (!signature.verify()) { + public void close() throws IOException { + try { + if (!getStatus().isOK()) { + return; + } + + if (signaturesToVerify.isEmpty()) { + return; + } + + PGPPublicKeyStore keyStore = new PGPPublicKeyStore(); + for (Entry<PGPSignature, List<PGPContentVerifier>> entry : signaturesToVerify.entrySet()) { + PGPSignature signature = entry.getKey(); + List<PGPContentVerifier> verifiers = entry.getValue(); + boolean verified = false; + for (PGPContentVerifier verifier : verifiers) { + try { + verifier.getOutputStream().write(signature.getSignatureTrailer()); + if (verifier.verify(signature.getSignature())) { + PGPPublicKey verifyingKey = verifierKeys.get(verifier); + if (!Boolean.FALSE.toString() + .equalsIgnoreCase(System.getProperty("p2.pgp.verifyExpiration"))) { //$NON-NLS-1$ + if (PGPPublicKeyService.compareSignatureTimeToKeyValidityTime(signature, + verifyingKey) != 0) { + LogHelper.log(new Status(IStatus.WARNING, Activator.ID, + NLS.bind(Messages.Error_SignatureAfterKeyExpiration, PGPPublicKeyService + .toHexFingerprint(verifyingKey)))); + } + } + + if (!Boolean.FALSE.toString() + .equalsIgnoreCase(System.getProperty("p2.pgp.verifyRevocation"))) { //$NON-NLS-1$ + if (!keyService.isCreatedBeforeRevocation(signature, verifyingKey)) { + setStatus(new Status(IStatus.ERROR, Activator.ID, + NLS.bind(Messages.Error_SignatureAfterKeyRevocation, PGPPublicKeyService + .toHexFingerprint(verifyingKey)))); + return; + } + } + + keyStore.addKey(verifyingKey); + verified = true; + break; + } + } catch (PGPException ex) { + LogHelper.log(new Status(IStatus.ERROR, Activator.ID, ex.getMessage(), ex)); + } + } + + if (!verified) { setStatus(new Status(IStatus.ERROR, Activator.ID, Messages.Error_SignatureAndFileDontMatch)); return; } - } catch (PGPException ex) { - setStatus(new Status(IStatus.ERROR, Activator.ID, ex.getMessage(), ex)); - return; } + + // Update the destination artifact descriptor with the signatures that have been + // verified and the keys used for that verification. + OutputStream destination = getDestination(); + if (destination instanceof IAdaptable) { + ArtifactDescriptor destinationDescriptor = ((IAdaptable) destination) + .getAdapter(ArtifactDescriptor.class); + destinationDescriptor.setProperty(PGP_SIGNATURES_PROPERTY_NAME, + sourceDescriptor.getProperty(PGP_SIGNATURES_PROPERTY_NAME)); + destinationDescriptor.setProperty(PGP_SIGNER_KEYS_PROPERTY_NAME, keyStore.toArmoredString()); + } + + setStatus(Status.OK_STATUS); + } finally + + { + super.close(); } - setStatus(Status.OK_STATUS); } } diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/messages.properties b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/messages.properties index 90d95f756..09a1e7292 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/messages.properties +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/messages.properties @@ -9,6 +9,8 @@ # SPDX-License-Identifier: EPL-2.0 ############################################################################### -Error_SignatureAndFileDontMatch=Signature is invalid for current content. -Error_CouldNotLoadSignature=Could not load signature -Error_publicKeyNotFound=Public key not found for {0}. +Error_SignatureAfterKeyRevocation=The signature is created after the revocation time of the signature's key {0} +Error_SignatureAfterKeyExpiration=The signature is created after the expiration time of the signature's key {0} +Error_SignatureAndFileDontMatch=The signature is invalid for current content +Error_CouldNotLoadSignature=The signatures could not be loaded +Warning_publicKeyNotFound=A public key with ID {0} needed to verify the signatures of {1} could not be found diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/MirrorRequest.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/MirrorRequest.java index d9f4dfe22..ccd932110 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/MirrorRequest.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/MirrorRequest.java @@ -23,7 +23,6 @@ import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.*; -import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactDescriptor; import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; import org.eclipse.equinox.internal.p2.repository.Transport; @@ -201,14 +200,6 @@ public class MirrorRequest extends ArtifactRequest { ((ArtifactDescriptor) destinationDescriptor).addProperties(targetDescriptorProperties); if (targetRepositoryProperties != null && destinationDescriptor instanceof SimpleArtifactDescriptor) ((SimpleArtifactDescriptor) destinationDescriptor).addRepositoryProperties(targetRepositoryProperties); - if (isCanonical && destinationDescriptor instanceof ArtifactDescriptor) { - // keep some safety properties - if (sourceDescriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME) != null) { - ((ArtifactDescriptor) destinationDescriptor) - .addProperties(Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, - sourceDescriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME))); - } - } return destinationDescriptor; } diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java index d627642d0..b13c03692 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/simple/SimpleArtifactRepository.java @@ -121,7 +121,7 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme private long cacheTimestamp = 0l; - public class ArtifactOutputStream extends OutputStream implements IStateful { + public class ArtifactOutputStream extends OutputStream implements IStateful, IAdaptable { private boolean closed; private long count = 0; private IArtifactDescriptor descriptor; @@ -205,6 +205,14 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme public void setFirstLink(OutputStream value) { firstLink = value; } + + @Override + public <T> T getAdapter(Class<T> adapter) { + if (adapter.isInstance(descriptor)) { + return adapter.cast(descriptor); + } + return null; + } } // TODO: optimize @@ -506,7 +514,7 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme Set<String> skipChecksums = DOWNLOAD_MD5_CHECKSUM_ENABLED ? Collections.emptySet() : Collections.singleton(ChecksumHelper.MD5); ArrayList<ProcessingStep> downloadChecksumSteps = new ArrayList<>(); addChecksumVerifiers(descriptor, downloadChecksumSteps, skipChecksums, IArtifactDescriptor.DOWNLOAD_CHECKSUM); - if (downloadChecksumSteps.isEmpty()) { + if (downloadChecksumSteps.isEmpty() && !isLocal()) { LogHelper.log(new Status(IStatus.WARNING, Activator.ID, NLS.bind(Messages.noDigestAlgorithmToVerifyDownload, descriptor.getArtifactKey()))); } diff --git a/bundles/org.eclipse.equinox.p2.console/pom.xml b/bundles/org.eclipse.equinox.p2.console/pom.xml index b069fcb80..6be37ac24 100644 --- a/bundles/org.eclipse.equinox.p2.console/pom.xml +++ b/bundles/org.eclipse.equinox.p2.console/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF index fdd4ec76f..73462bd3e 100644 --- a/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.core;singleton:=true -Bundle-Version: 2.8.100.qualifier +Bundle-Version: 2.9.100.qualifier Bundle-ClassPath: . Bundle-Activator: org.eclipse.equinox.internal.p2.core.Activator Bundle-Vendor: %providerName @@ -64,14 +64,16 @@ Export-Package: org.eclipse.equinox.internal.p2.core;x-friends:="org.eclipse.equ org.eclipse.equinox.p2.updatesite, org.eclipse.equinox.p2.director.app, org.eclipse.equinox.p2.transport.ecf", - org.eclipse.equinox.p2.core;version="2.7.0", - org.eclipse.equinox.p2.core.spi;version="2.1.0" -Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)" + org.eclipse.equinox.p2.core;version="2.8.0";uses:="org.eclipse.core.runtime", + org.eclipse.equinox.p2.core.spi;version="2.1.0";uses:="org.eclipse.equinox.p2.core" Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Service-Component: OSGI-INF/eventBus.xml, OSGI-INF/agentProvider.xml Import-Package: org.bouncycastle.bcpg;version="1.65.0", org.bouncycastle.openpgp;version="1.65.0", + org.eclipse.core.runtime;common=split;version="[3.5.0,4.0.0)", + org.eclipse.equinox.p2.core;version="[2.8.0,2.9.0)";resolution:=optional, + org.eclipse.equinox.p2.core.spi;version="[2.1.0,2.2.0)";resolution:=optional, org.eclipse.osgi.framework.eventmgr;version="1.2.0", org.eclipse.osgi.framework.log;version="1.0.0", org.eclipse.osgi.service.debug;version="1.0.0", diff --git a/bundles/org.eclipse.equinox.p2.core/META-INF/p2.inf b/bundles/org.eclipse.equinox.p2.core/META-INF/p2.inf new file mode 100644 index 000000000..0983c41b7 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.core/META-INF/p2.inf @@ -0,0 +1,5 @@ +# Workaround https://bugs.eclipse.org/bugs/show_bug.cgi?id=525368 +# we hint about which bundle to download at install that is capable of +# resolving packages +requires.0.namespace = osgi.bundle +requires.0.name = org.eclipse.equinox.common
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.core/pom.xml b/bundles/org.eclipse.equinox.p2.core/pom.xml index 4126209ae..3913f722d 100644 --- a/bundles/org.eclipse.equinox.p2.core/pom.xml +++ b/bundles/org.eclipse.equinox.p2.core/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.core</artifactId> - <version>2.8.100-SNAPSHOT</version> + <version>2.9.100-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/internal/p2/core/helpers/SecureXMLUtil.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/internal/p2/core/helpers/SecureXMLUtil.java index 202921c17..0afeb2936 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/internal/p2/core/helpers/SecureXMLUtil.java +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/internal/p2/core/helpers/SecureXMLUtil.java @@ -16,7 +16,6 @@ package org.eclipse.equinox.internal.p2.core.helpers; import javax.xml.XMLConstants; import javax.xml.parsers.*; import org.xml.sax.*; -import org.xml.sax.helpers.XMLReaderFactory; /** * A utility class for creating an XML-related factories suitable for @@ -52,10 +51,11 @@ public class SecureXMLUtil { * Create a new {@link XMLReader}. * * @throws SAXException + * @throws ParserConfigurationException */ - public static XMLReader newSecureXMLReader() throws SAXException { - XMLReader reader = XMLReaderFactory.createXMLReader(); - reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - return reader; + public static XMLReader newSecureXMLReader() throws SAXException, ParserConfigurationException { + SAXParserFactory factory = newSecureSAXParserFactory(); + factory.setNamespaceAware(true); + return factory.newSAXParser().getXMLReader(); } } diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/UIServices.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/UIServices.java index 7a29b1d9b..5be838d4b 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/UIServices.java +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/UIServices.java @@ -73,13 +73,14 @@ public abstract class UIServices { } /** - * Trust information returned from a trust request. * + * Trust information returned from a trust request. */ public static class TrustInfo { private final Certificate[] trustedCertificates; private final Collection<PGPPublicKey> trustedPGPKeys; private final boolean saveTrustedCertificates; private final boolean trustUnsigned; + private final boolean trustAlways; /** * @@ -96,6 +97,7 @@ public abstract class UIServices { this.trustedPGPKeys = Collections.emptyList(); this.saveTrustedCertificates = save; this.trustUnsigned = trustUnsigned; + trustAlways = false; } /** @@ -112,6 +114,28 @@ public abstract class UIServices { public TrustInfo(Collection<Certificate> trustedCertificates, Collection<PGPPublicKey> trustedPGPKeys, boolean save, boolean trustUnsigned) { + this(trustedCertificates, trustedPGPKeys, save, trustUnsigned, false); + } + + /** + * @param trustedCertificates Trusted certificates + * @param trustedPGPKeys Trusted PGP public keys + * @param save Whether to store trusted certificates and keys or + * not. + * @param trustUnsigned Whether to trust unsigned content. + * <code>true</code> if installation should continue + * despite unsigned content; <code>false</code> + * otherwise. + * @param trustAlways Whether to always trust all content regardless of + * whether it's signed, regardless of how it's + * signed, and regardless of the certificate or key + * with which it's signed. + * + * @since 2.9 + */ + public TrustInfo(Collection<Certificate> trustedCertificates, Collection<PGPPublicKey> trustedPGPKeys, + boolean save, boolean trustUnsigned, boolean trustAlways) { + this.trustAlways = trustAlways; this.trustedCertificates = trustedCertificates.toArray(Certificate[]::new); this.trustedPGPKeys = trustedPGPKeys; this.saveTrustedCertificates = save; @@ -131,7 +155,10 @@ public abstract class UIServices { /** * - * @return the trusted PGP keys + * Return a collection of the keys that should be trusted for the requested + * operation. + * + * @return the trusted PGP keys. * @since 2.8 */ public Collection<PGPPublicKey> getTrustedPGPKeys() { @@ -159,6 +186,20 @@ public abstract class UIServices { public boolean trustUnsignedContent() { return trustUnsigned; } + + /** + * Return a boolean indicating whether to always trust all content regardless of + * whether it's signed, regardless of how it's signed, and regardless of the + * certificate or key with which it's signed, both during this operation and for + * all future operations. + * + * @return <code>true</code> if all content should always be trusted, and + * <code>false</code> otherwise. + * @since 2.9 + */ + public boolean trustAlways() { + return trustAlways; + } } /** diff --git a/bundles/org.eclipse.equinox.p2.director.app/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.director.app/META-INF/MANIFEST.MF index 04b4a7068..7cbc86f19 100644 --- a/bundles/org.eclipse.equinox.p2.director.app/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.director.app/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.director.app;singleton:=true -Bundle-Version: 1.2.0.qualifier +Bundle-Version: 1.2.100.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.director.app.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -10,14 +10,15 @@ Export-Package: org.eclipse.equinox.internal.p2.director.app;x-internal:=true Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)" Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy -Import-Package: org.eclipse.equinox.app, +Import-Package: org.bouncycastle.openpgp, + org.eclipse.equinox.app, org.eclipse.equinox.internal.p2.core.helpers, org.eclipse.equinox.internal.p2.director, org.eclipse.equinox.internal.p2.engine, org.eclipse.equinox.internal.provisional.p2.core.eventbus, org.eclipse.equinox.internal.provisional.p2.director, org.eclipse.equinox.internal.provisional.p2.repository, - org.eclipse.equinox.p2.core;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.core;version="[2.8.0,3.0.0)", org.eclipse.equinox.p2.core.spi;version="[2.1.0,3.0.0)", org.eclipse.equinox.p2.engine;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.engine.query;version="[2.0.0,3.0.0)", diff --git a/bundles/org.eclipse.equinox.p2.director.app/pom.xml b/bundles/org.eclipse.equinox.p2.director.app/pom.xml index 0ee657185..44f4bde1c 100644 --- a/bundles/org.eclipse.equinox.p2.director.app/pom.xml +++ b/bundles/org.eclipse.equinox.p2.director.app/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.director.app</artifactId> - <version>1.2.0-SNAPSHOT</version> + <version>1.2.100-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java index d163af047..4710d4cc1 100644 --- a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java +++ b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java @@ -32,6 +32,7 @@ import java.util.*; import java.util.Map.Entry; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.bouncycastle.openpgp.PGPPublicKey; import org.eclipse.core.runtime.*; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; @@ -85,6 +86,21 @@ public class DirectorApplication implements IApplication, ProvisioningListener { } return new TrustInfo(trusted, false, true); } + + @Override + public TrustInfo getTrustInfo(Certificate[][] untrustedChains, Collection<PGPPublicKey> untrustedPGPKeys, + String[] unsignedDetail) { + final Collection<Certificate> trusted; + if (untrustedChains == null) { + trusted = List.of(); + } else { + trusted = new ArrayList<>(untrustedChains.length); + for (Certificate[] untrustedChain : untrustedChains) { + trusted.add(untrustedChain[0]); + } + } + return new TrustInfo(trusted, untrustedPGPKeys, false, true); + } } class LocationQueryable implements IQueryable<IInstallableUnit> { diff --git a/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF index 79fb04c24..8b2a73e25 100644 --- a/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.director;singleton:=true -Bundle-Version: 2.5.100.qualifier +Bundle-Version: 2.5.200.qualifier Bundle-ClassPath: . Bundle-Activator: org.eclipse.equinox.internal.p2.director.DirectorActivator Bundle-Vendor: %providerName diff --git a/bundles/org.eclipse.equinox.p2.director/pom.xml b/bundles/org.eclipse.equinox.p2.director/pom.xml index 935a2cc28..0a32c8db0 100644 --- a/bundles/org.eclipse.equinox.p2.director/pom.xml +++ b/bundles/org.eclipse.equinox.p2.director/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.director</artifactId> - <version>2.5.100-SNAPSHOT</version> + <version>2.5.200-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/DirectorActivator.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/DirectorActivator.java index 7d331936f..a2960e384 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/DirectorActivator.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/DirectorActivator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2017 IBM Corporation and others. + * Copyright (c) 2007, 2022 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,24 +10,26 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Christoph Läubrich - access activator static singelton in a safe way *******************************************************************************/ package org.eclipse.equinox.internal.p2.director; +import java.util.Optional; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; public class DirectorActivator implements BundleActivator { public static final String PI_DIRECTOR = "org.eclipse.equinox.p2.director"; //$NON-NLS-1$ - public static BundleContext context; + public static volatile Optional<BundleContext> context = Optional.empty(); @Override public void start(BundleContext ctx) throws Exception { - context = ctx; + context = Optional.of(ctx); } @Override public void stop(BundleContext ctx) throws Exception { - DirectorActivator.context = null; + DirectorActivator.context = Optional.empty(); } } diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java index ae30164ad..458a8fb86 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2020 IBM Corporation and others. + * Copyright (c) 2007, 2022 IBM Corporation and others. * * This * program and the accompanying materials are made available under the terms of @@ -17,6 +17,7 @@ * Sonatype, Inc. - ongoing development * Rapicorp, Inc. - split the optimization function * Red Hat, Inc. - support for remediation page + * Christoph Läubrich - access activator static singelton in a safe way ******************************************************************************/ package org.eclipse.equinox.internal.p2.director; @@ -207,7 +208,8 @@ public class Projector { // allow the user to specify a longer timeout. // only set the value if it is a positive integer larger than the default. // see https://bugs.eclipse.org/336967 - timeoutString = DirectorActivator.context.getProperty(PROP_PROJECTOR_TIMEOUT); + timeoutString = DirectorActivator.context.map(ctx -> ctx.getProperty(PROP_PROJECTOR_TIMEOUT)) + .orElse(null); if (timeoutString != null) timeout = Math.max(timeout, Integer.parseInt(timeoutString)); } catch (Exception e) { diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/PlanExecutionHelper.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/PlanExecutionHelper.java index 4c51ca1a9..83ac500b7 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/PlanExecutionHelper.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/provisional/p2/director/PlanExecutionHelper.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2015 IBM Corporation and others. + * Copyright (c) 2009, 2022 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,6 +11,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Red Hat Inc. - Bug 460967 + * Christoph Läubrich - access activator static singelton in a safe way *******************************************************************************/ package org.eclipse.equinox.internal.provisional.p2.director; @@ -36,7 +37,8 @@ public class PlanExecutionHelper { .getService(IEngine.class).perform(result.getInstallerPlan(), phaseSet, progress); if (!installerPlanStatus.isOK()) return installerPlanStatus; - Configurator configChanger = ServiceHelper.getService(DirectorActivator.context, Configurator.class); + Configurator configChanger = DirectorActivator.context + .map(ctx -> ServiceHelper.getService(ctx, Configurator.class)).orElse(null); try { configChanger.applyConfiguration(); } catch (IOException e) { diff --git a/bundles/org.eclipse.equinox.p2.directorywatcher/pom.xml b/bundles/org.eclipse.equinox.p2.directorywatcher/pom.xml index 5505474fc..ece589593 100644 --- a/bundles/org.eclipse.equinox.p2.directorywatcher/pom.xml +++ b/bundles/org.eclipse.equinox.p2.directorywatcher/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.discovery.compatibility/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.discovery.compatibility/META-INF/MANIFEST.MF index 277ae2f5f..fbfa7e023 100644 --- a/bundles/org.eclipse.equinox.p2.discovery.compatibility/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.discovery.compatibility/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.equinox.p2.discovery.compatibility;singleton:=true -Bundle-Version: 1.2.100.qualifier +Bundle-Version: 1.2.200.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.core.runtime;bundle-version="3.3.0", diff --git a/bundles/org.eclipse.equinox.p2.discovery.compatibility/pom.xml b/bundles/org.eclipse.equinox.p2.discovery.compatibility/pom.xml index 23c6cc4d1..b926dd577 100644 --- a/bundles/org.eclipse.equinox.p2.discovery.compatibility/pom.xml +++ b/bundles/org.eclipse.equinox.p2.discovery.compatibility/pom.xml @@ -14,11 +14,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.discovery.compatibility</artifactId> - <version>1.2.100-SNAPSHOT</version> + <version>1.2.200-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.discovery.compatibility/src/org/eclipse/equinox/internal/p2/discovery/compatibility/DirectoryParser.java b/bundles/org.eclipse.equinox.p2.discovery.compatibility/src/org/eclipse/equinox/internal/p2/discovery/compatibility/DirectoryParser.java index 97a21448c..9cbb8d0a3 100644 --- a/bundles/org.eclipse.equinox.p2.discovery.compatibility/src/org/eclipse/equinox/internal/p2/discovery/compatibility/DirectoryParser.java +++ b/bundles/org.eclipse.equinox.p2.discovery.compatibility/src/org/eclipse/equinox/internal/p2/discovery/compatibility/DirectoryParser.java @@ -16,6 +16,7 @@ package org.eclipse.equinox.internal.p2.discovery.compatibility; import java.io.IOException; import java.io.Reader; +import javax.xml.parsers.ParserConfigurationException; import org.eclipse.equinox.internal.p2.core.helpers.SecureXMLUtil; import org.eclipse.equinox.internal.p2.discovery.compatibility.Directory.Entry; import org.eclipse.equinox.internal.p2.discovery.compatibility.util.DefaultSaxErrorHandler; @@ -42,7 +43,7 @@ public class DirectoryParser { XMLReader xmlReader; try { xmlReader = SecureXMLUtil.newSecureXMLReader(); - } catch (SAXException e) { + } catch (SAXException | ParserConfigurationException e) { throw new IOWithCauseException(e.getMessage(), e); } xmlReader.setErrorHandler(new DefaultSaxErrorHandler()); diff --git a/bundles/org.eclipse.equinox.p2.discovery/pom.xml b/bundles/org.eclipse.equinox.p2.discovery/pom.xml index 6930630fe..08c1a9a53 100644 --- a/bundles/org.eclipse.equinox.p2.discovery/pom.xml +++ b/bundles/org.eclipse.equinox.p2.discovery/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.engine/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.engine/META-INF/MANIFEST.MF index d1e7137aa..561283093 100644 --- a/bundles/org.eclipse.equinox.p2.engine/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.engine/META-INF/MANIFEST.MF @@ -46,9 +46,10 @@ Import-Package: javax.xml.parsers, org.eclipse.equinox.p2.query;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.artifact;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.repository.artifact.spi;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.metadata;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.metadata.spi;version="[2.0.0,3.0.0)", - org.eclipse.equinox.p2.repository.spi;version="2.0.0", + org.eclipse.equinox.p2.repository.spi;version="[2.0.0,3.0.0)", org.eclipse.osgi.service.datalocation;version="1.0.0", org.eclipse.osgi.service.debug;version="1.1.0", org.eclipse.osgi.service.security;version="1.0.0", diff --git a/bundles/org.eclipse.equinox.p2.engine/plugin.xml b/bundles/org.eclipse.equinox.p2.engine/plugin.xml index 3d469f492..cee2c3856 100644 --- a/bundles/org.eclipse.equinox.p2.engine/plugin.xml +++ b/bundles/org.eclipse.equinox.p2.engine/plugin.xml @@ -4,6 +4,7 @@ <extension-point id="touchpoints" name="Touchpoints" schema="schema/touchpoints.exsd"/> <extension-point id="actions" name="Actions" schema="schema/actions.exsd"/> +<extension-point id="pgp" name="PGP" schema="schema/pgp.exsd"/> <extension id="metadataRepository" point="org.eclipse.equinox.p2.metadata.repository.metadataRepositories"> diff --git a/bundles/org.eclipse.equinox.p2.engine/pom.xml b/bundles/org.eclipse.equinox.p2.engine/pom.xml index cbf8386be..eb9562492 100644 --- a/bundles/org.eclipse.equinox.p2.engine/pom.xml +++ b/bundles/org.eclipse.equinox.p2.engine/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.engine/schema/pgp.exsd b/bundles/org.eclipse.equinox.p2.engine/schema/pgp.exsd new file mode 100644 index 000000000..fcb06d04a --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.engine/schema/pgp.exsd @@ -0,0 +1,107 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.equinox.p2.engine" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.equinox.p2.engine" id="pgp" name="PGP"/> + </appInfo> + <documentation> + Configure PGP keys and trust for p2 operations + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appInfo> + <meta.element /> + </appInfo> + </annotation> + <complexType> + <sequence minOccurs="0" maxOccurs="unbounded"> + <element ref="trustedKeys"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="trustedKeys"> + <annotation> + <documentation> + PGP Keys that are trusted by default. + </documentation> + </annotation> + <complexType> + <attribute name="path" type="string"> + <annotation> + <documentation> + Path to a file containing PGP keys in armored form. + </documentation> + <appInfo> + <meta.attribute kind="resource"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + 2.7.300 + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + [Enter extension point usage example here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiinfo"/> + </appInfo> + <documentation> + [Enter API information here.] + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + [Enter information about supplied implementation of this extension point.] + </documentation> + </annotation> + + +</schema> diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CertificateChecker.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CertificateChecker.java index 15943a47c..3a1589f8b 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CertificateChecker.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CertificateChecker.java @@ -13,14 +13,17 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.engine.phases; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.net.URL; +import java.nio.ByteBuffer; import java.security.GeneralSecurityException; -import java.security.cert.Certificate; +import java.security.cert.*; import java.util.*; import java.util.Map.Entry; +import java.util.function.Supplier; import java.util.stream.Collectors; -import org.bouncycastle.openpgp.*; +import java.util.stream.Stream; +import org.bouncycastle.openpgp.PGPPublicKey; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPPublicKeyStore; @@ -28,13 +31,16 @@ import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerif import org.eclipse.equinox.internal.p2.engine.*; import org.eclipse.equinox.p2.core.*; import org.eclipse.equinox.p2.core.UIServices.TrustInfo; -import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.engine.IProfile; +import org.eclipse.equinox.p2.engine.ProfileScope; +import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; +import org.eclipse.equinox.p2.repository.artifact.spi.IArtifactUIServices; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.osgi.service.security.TrustEngine; import org.eclipse.osgi.signedcontent.*; import org.eclipse.osgi.util.NLS; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; +import org.osgi.framework.*; import org.osgi.service.prefs.BackingStoreException; import org.osgi.util.tracker.ServiceTracker; @@ -46,15 +52,49 @@ import org.osgi.util.tracker.ServiceTracker; public class CertificateChecker { private static final String DEBUG_PREFIX = "certificate checker"; //$NON-NLS-1$ + public static final String TRUST_ALWAYS_PROPERTY = "trustAlways"; //$NON-NLS-1$ + public static final String TRUSTED_KEY_STORE_PROPERTY = "pgp.trustedPublicKeys"; //$NON-NLS-1$ + public static final String TRUSTED_CERTIFICATES_PROPERTY = "trustedCertificates"; //$NON-NLS-1$ + + /*** + * Store the optional profile for PGP key handling + */ + private IProfile profile; + /** * Stores artifacts to check */ private Map<IArtifactDescriptor, File> artifacts = new HashMap<>(); private final IProvisioningAgent agent; + private final PGPPublicKeyService keyService; - private PGPPublicKeyStore trustedKeys; + // Lazily loading + private Supplier<PGPPublicKeyStore> trustedKeys = new Supplier<>() { + private PGPPublicKeyStore cache = null; + + public PGPPublicKeyStore get() { + if (cache == null) { + cache = getPreferenceTrustedKeys(); + getContributedTrustedKeys().keySet().forEach(cache::addKey); + } + return cache; + } + }; + + // Lazily loading in case we ever add an extension point for registering + // certificates. + private Supplier<Collection<? extends Certificate>> additionalTrustedCertificates = new Supplier<>() { + private Collection<? extends Certificate> cache = null; + + public Collection<? extends Certificate> get() { + if (cache == null) { + cache = getPreferenceTrustedCertificates(); + } + return cache; + } + }; public CertificateChecker() { this(null); @@ -63,11 +103,14 @@ public class CertificateChecker { public CertificateChecker(IProvisioningAgent agent) { this.agent = agent; artifacts = new HashMap<>(); + keyService = agent.getService(PGPPublicKeyService.class); + } public IStatus start() { final BundleContext context = EngineActivator.getContext(); - ServiceReference<SignedContentFactory> contentFactoryRef = context.getServiceReference(SignedContentFactory.class); + ServiceReference<SignedContentFactory> contentFactoryRef = context + .getServiceReference(SignedContentFactory.class); SignedContentFactory verifierFactory = context.getService(contentFactoryRef); try { return checkCertificates(verifierFactory); @@ -77,72 +120,95 @@ public class CertificateChecker { } private IStatus checkCertificates(SignedContentFactory verifierFactory) { + if (artifacts.isEmpty()) { + return Status.OK_STATUS; + } + + // If unsigned content is allowed then the flood gates are open so there is no + // point in checking for unrooted certificates nor for not-yet-trusted keys. + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=578583 + String policy = getUnsignedContentPolicy(); + if (EngineActivator.UNSIGNED_ALLOW.equals(policy)) { + return Status.OK_STATUS; + } + UIServices serviceUI = agent.getService(UIServices.class); - ArrayList<Certificate> untrustedCertificates = new ArrayList<>(); - Map<IArtifactDescriptor, Collection<PGPPublicKey>> untrustedPGPArtifacts = new HashMap<>(); - Map<IArtifactDescriptor, File> unsigned = new HashMap<>(); - ArrayList<Certificate[]> untrustedChain = new ArrayList<>(); - Map<Certificate, Collection<File>> untrustedArtifacts = new HashMap<>(); - IStatus status = Status.OK_STATUS; - if (artifacts.isEmpty() || serviceUI == null) { - return status; + if (serviceUI == null) { + return Status.OK_STATUS; + } + + if (isTrustAlways()) { + return Status.OK_STATUS; } - Set<Long> trustedKeysIds = new HashSet<>(); + + IArtifactUIServices artifactServiceUI = serviceUI instanceof IArtifactUIServices + ? (IArtifactUIServices) serviceUI + : (untrustedCertificateChains, untrustedPGPKeys, unsignedArtifacts, + artifactFiles) -> IArtifactUIServices.getTrustInfo(serviceUI, untrustedCertificateChains, + untrustedPGPKeys, unsignedArtifacts, artifactFiles); + + Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificates = new LinkedHashMap<>(); + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys = new LinkedHashMap<>(); + Set<IArtifactKey> unsignedArtifacts = new LinkedHashSet<>(); + Set<PGPPublicKey> trustedKeySet = new HashSet<>(); + boolean isTrustedKeySetInitialized = false; + Map<IArtifactKey, File> artifactFiles = new LinkedHashMap<>(); for (Entry<IArtifactDescriptor, File> artifact : artifacts.entrySet()) { + IArtifactDescriptor artifactDescriptor = artifact.getKey(); + IArtifactKey artifactKey = artifactDescriptor.getArtifactKey(); File artifactFile = artifact.getValue(); + artifactFiles.put(artifactKey, artifactFile); try { SignedContent content = verifierFactory.getSignedContent(artifactFile); if (content.isSigned()) { SignerInfo[] signerInfo = content.getSignerInfos(); - if (Arrays.stream(signerInfo).noneMatch(SignerInfo::isTrusted)) { - // Only record the untrusted elements if there are no trusted elements. + // Only record the untrusted elements if there are no trusted elements. + // Also check previously trusted certificates from the preferences. + if (Arrays.stream(signerInfo).noneMatch(SignerInfo::isTrusted) + && Arrays.stream(signerInfo).map(SignerInfo::getCertificateChain).flatMap(Arrays::stream) + .noneMatch(cert -> additionalTrustedCertificates.get().contains(cert))) { for (SignerInfo element : signerInfo) { if (!element.isTrusted()) { - Certificate[] certificateChain = element.getCertificateChain(); - if (!untrustedCertificates.contains(certificateChain[0])) { - untrustedCertificates.add(certificateChain[0]); - untrustedChain.add(certificateChain); - } - if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNTRUSTED) { - untrustedArtifacts.computeIfAbsent(certificateChain[0], key -> new ArrayList<>()) - .add(artifactFile); - } + List<Certificate> certificateChain = Arrays.asList(element.getCertificateChain()); + untrustedCertificates.computeIfAbsent(certificateChain, key -> new LinkedHashSet<>()) + .add(artifactKey); } } } } else { - Collection<PGPSignature> signatures = PGPSignatureVerifier.getSignatures(artifact.getKey()); - if (!signatures.isEmpty()) { - if (trustedKeys == null) { - trustedKeys = buildPGPTrustore(); - } - if (trustedKeysIds.isEmpty() && !trustedKeys.isEmpty()) { - trustedKeysIds.addAll(trustedKeys.all().stream() - .map(PGPPublicKey::getKeyID).map(Long::valueOf).collect(Collectors.toSet())); + // The keys are in this destination artifact's properties if and only if the + // PGPSignatureVerifier verified the signatures against these keys. + List<PGPPublicKey> verifiedKeys = PGPPublicKeyStore + .readPublicKeys( + artifactDescriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)) + .stream().map(keyService::addKey).collect(Collectors.toList()); + if (!verifiedKeys.isEmpty()) { + if (!isTrustedKeySetInitialized) { + isTrustedKeySetInitialized = true; + trustedKeySet.addAll(trustedKeys.get().all()); } - if (signatures.stream().map(PGPSignature::getKeyID).noneMatch(trustedKeysIds::contains)) { - untrustedPGPArtifacts.put(artifact.getKey(), - signatures.stream().map(PGPSignature::getKeyID) - .map(id -> findKey(id, artifact.getKey())) - .filter(Objects::nonNull) - .collect(Collectors.toList())); + // Only record the untrusted keys if none of the keys are trusted. + if (verifiedKeys.stream().noneMatch(trustedKeySet::contains)) { + verifiedKeys.forEach(key -> untrustedPGPKeys + .computeIfAbsent(key, k -> new LinkedHashSet<>()).add(artifactKey)); } } else { - unsigned.put(artifact.getKey(), artifactFile); + unsignedArtifacts.add(artifactKey); } } - } catch (GeneralSecurityException | PGPException e) { + } catch (GeneralSecurityException e) { return new Status(IStatus.ERROR, EngineActivator.ID, Messages.CertificateChecker_SignedContentError, e); } catch (IOException e) { - return new Status(IStatus.ERROR, EngineActivator.ID, Messages.CertificateChecker_SignedContentIOError, e); + return new Status(IStatus.ERROR, EngineActivator.ID, Messages.CertificateChecker_SignedContentIOError, + e); } } // log the unsigned artifacts if requested - if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNSIGNED && !unsigned.isEmpty()) { + if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNSIGNED && !unsignedArtifacts.isEmpty()) { StringBuilder message = new StringBuilder("The following artifacts are unsigned:\n"); //$NON-NLS-1$ - for (File file : unsigned.values()) { - message.append(NLS.bind(" {0}\n", file.getPath())); //$NON-NLS-1$ + for (IArtifactKey key : unsignedArtifacts) { + message.append(NLS.bind(" {0}\n", artifactFiles.get(key).getPath())); //$NON-NLS-1$ } DebugHelper.debug(DEBUG_PREFIX, message.toString()); } @@ -150,130 +216,161 @@ public class CertificateChecker { // log the untrusted certificates if requested if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNTRUSTED && !untrustedCertificates.isEmpty()) { StringBuilder message = new StringBuilder("The following certificates are untrusted:\n"); //$NON-NLS-1$ - for (Certificate cert : untrustedArtifacts.keySet()) { - message.append(cert.toString() + "\n"); //$NON-NLS-1$ + for (Map.Entry<List<Certificate>, Set<IArtifactKey>> entry : untrustedCertificates.entrySet()) { + message.append(entry.getKey().get(0).toString() + "\n"); //$NON-NLS-1$ message.append(" used by the following artifacts:\n"); //$NON-NLS-1$ - for (File file : untrustedArtifacts.get(cert)) { - message.append(NLS.bind(" {0}\n", file.getPath())); //$NON-NLS-1$ + for (IArtifactKey key : entry.getValue()) { + message.append(NLS.bind(" {0}\n", artifactFiles.get(key).getPath())); //$NON-NLS-1$ } + } DebugHelper.debug(DEBUG_PREFIX, message.toString()); } - Set<PGPPublicKey> untrustedPGPKeys = untrustedPGPArtifacts.values().stream().flatMap(Collection::stream) - .collect(Collectors.toSet()); + if (DebugHelper.DEBUG_CERTIFICATE_CHECKER_UNTRUSTED && !untrustedPGPKeys.isEmpty()) { StringBuilder message = new StringBuilder("The following PGP Keys are untrusted:\n"); //$NON-NLS-1$ - for (PGPPublicKey untrustedKey : untrustedPGPKeys) { - message.append(Long.toHexString(untrustedKey.getKeyID()) + "\n"); //$NON-NLS-1$ + for (Map.Entry<PGPPublicKey, Set<IArtifactKey>> entry : untrustedPGPKeys.entrySet()) { + message.append(Long.toHexString(entry.getKey().getKeyID()) + "\n"); //$NON-NLS-1$ message.append(" used by the following artifacts:\n"); //$NON-NLS-1$ - for (Entry<IArtifactDescriptor, Collection<PGPPublicKey>> entry : untrustedPGPArtifacts.entrySet()) { - if (entry.getValue().stream().anyMatch(signer -> signer.getKeyID() == untrustedKey.getKeyID())) { - message.append(NLS.bind(" {0}\n", entry.getKey().getArtifactKey())); //$NON-NLS-1$ - } + for (IArtifactKey key : entry.getValue()) { + message.append(NLS.bind(" {0}\n", key)); //$NON-NLS-1$ } } DebugHelper.debug(DEBUG_PREFIX, message.toString()); } - String policy = getUnsignedContentPolicy(); - //if there is unsigned content and we should never allow it, then fail without further checking certificates - if (!unsigned.isEmpty() && EngineActivator.UNSIGNED_FAIL.equals(policy)) { - return new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.CertificateChecker_UnsignedNotAllowed, unsigned)); + // if there is unsigned content and we should never allow it, then fail without + // further checking certificates + if (!unsignedArtifacts.isEmpty() && EngineActivator.UNSIGNED_FAIL.equals(policy)) { + return new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind( + Messages.CertificateChecker_UnsignedNotAllowed, + unsignedArtifacts.stream().map(key -> artifactFiles.get(key)).collect(Collectors.toList()))); } - String[] details = EngineActivator.UNSIGNED_ALLOW.equals(policy) || unsigned.isEmpty() ? null - : unsigned.values().stream().map(Object::toString).toArray(String[]::new); - Certificate[][] unTrustedCertificateChains = untrustedCertificates.isEmpty() ? null - : untrustedChain.toArray(Certificate[][]::new); // If there was no unsigned content, and nothing untrusted, no need to prompt. - if (details == null && unTrustedCertificateChains == null && untrustedPGPArtifacts.isEmpty()) { - return status; + if (unsignedArtifacts.isEmpty() && untrustedCertificates.isEmpty() && untrustedPGPKeys.isEmpty()) { + return Status.OK_STATUS; } - TrustInfo trustInfo = serviceUI.getTrustInfo(unTrustedCertificateChains, - untrustedPGPKeys, - details); + TrustInfo trustInfo = artifactServiceUI.getTrustInfo(untrustedCertificates, untrustedPGPKeys, unsignedArtifacts, + artifactFiles); - // If user doesn't trust unsigned content, cancel the operation - if (!unsigned.isEmpty() && !trustInfo.trustUnsignedContent()) { - return Status.CANCEL_STATUS; - } + setTrustAlways(trustInfo.trustAlways()); - Certificate[] trustedCertificates = trustInfo.getTrustedCertificates(); - // If we had untrusted chains and nothing was trusted, cancel the operation - if (unTrustedCertificateChains != null && trustedCertificates == null) { - return new Status(IStatus.CANCEL, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); - } - // Anything that was trusted should be removed from the untrusted list - if (trustedCertificates != null) { - for (Certificate trustedCertificate : trustedCertificates) { - untrustedCertificates.remove(trustedCertificate); + if (!trustInfo.trustAlways()) { + // If there is unsigned content and user doesn't trust unsigned content, cancel + // the operation. + if (!unsignedArtifacts.isEmpty() && !trustInfo.trustUnsignedContent()) { + return Status.CANCEL_STATUS; } - } - trustedKeysIds - .addAll(trustInfo.getTrustedPGPKeys().stream().map(PGPPublicKey::getKeyID).collect(Collectors.toSet())); - untrustedPGPArtifacts.values().removeIf( - pgpKeys -> pgpKeys.stream().anyMatch(untrusted -> trustedKeysIds.contains(untrusted.getKeyID()))); - - // If there is still untrusted content, cancel the operation - if (!untrustedCertificates.isEmpty() || !untrustedPGPArtifacts.isEmpty()) { - return new Status(IStatus.CANCEL, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); - } - // If we should persist the trusted certificates, add them to the trust engine - if (trustInfo.persistTrust()) { - IStatus certifactesStatus = trustInfo.getTrustedCertificates().length == 0 ? null - : persistTrustedCertificates(trustedCertificates); - trustInfo.getTrustedPGPKeys().forEach(trustedKeys::addKey); - IStatus pgpStatus = trustInfo.getTrustedPGPKeys().isEmpty() ? null : persistTrustedKeys(trustedKeys); - if (pgpStatus == null) { - return certifactesStatus; + + Certificate[] trustedCertificates = trustInfo.getTrustedCertificates(); + // If we had untrusted chains and nothing was trusted, cancel the operation + if (!untrustedCertificates.isEmpty() && trustedCertificates == null) { + return new Status(IStatus.CANCEL, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); + } + + // Anything that was trusted should be removed from the untrusted list + if (trustedCertificates != null) { + List<Certificate> trustedCertificateList = Arrays.asList(trustedCertificates); + untrustedCertificates.keySet().removeIf(it -> trustedCertificateList.contains(it.get(0))); } - if (certifactesStatus == null) { - return pgpStatus; + + trustedKeySet.addAll(trustInfo.getTrustedPGPKeys()); + + Set<IArtifactKey> trustedArtifactKeys = trustedKeySet.stream().map(untrustedPGPKeys::get) + .filter(Objects::nonNull).flatMap(Set::stream).collect(Collectors.toSet()); + untrustedPGPKeys.values().forEach(it -> it.removeAll(trustedArtifactKeys)); + untrustedPGPKeys.values().removeIf(Collection::isEmpty); + + // If there is still untrusted content, cancel the operation + if (!untrustedCertificates.isEmpty() || !untrustedPGPKeys.isEmpty()) { + return new Status(IStatus.CANCEL, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); } - return new MultiStatus(getClass(), IStatus.OK, new IStatus[] { pgpStatus, certifactesStatus }, - pgpStatus.getMessage() + '\n' + certifactesStatus.getMessage(), null); } - return status; - } + // If we should persist the trusted certificates and keys. + if (trustInfo.persistTrust()) { + List<IStatus> statuses = new ArrayList<>(); + Set<Certificate> unsavedCertificates = new LinkedHashSet<>(); + if (trustInfo.getTrustedCertificates() != null) { + unsavedCertificates.addAll(Arrays.asList(trustInfo.getTrustedCertificates())); + } + + if (!unsavedCertificates.isEmpty()) { + // Try to save to the trust engine first. + IStatus status = persistTrustedCertificatesInTrustEngine(unsavedCertificates); + if (status != null && !status.isOK()) { + statuses.add(status); + } - private PGPPublicKey findKey(long id, IArtifactDescriptor artifact) { - PGPPublicKey key = PGPSignatureVerifier.KNOWN_KEYS.getKey(id); - if (key != null) { - return key; + // If we couldn't save them in the trust engine, save them in the preferences. + if (!unsavedCertificates.isEmpty()) { + // Be sure we add the new certificates to the previously saved certificates. + unsavedCertificates.addAll(getPreferenceTrustedCertificates()); + IStatus preferenceStatus = persistTrustedCertificates(unsavedCertificates); + if (preferenceStatus != null && !preferenceStatus.isOK()) { + statuses.add(preferenceStatus); + } + } + } + + if (!trustInfo.getTrustedPGPKeys().isEmpty()) { + // Be sure we add the new keys to the previously saved keys. + PGPPublicKeyStore keyStore = getPreferenceTrustedKeys(); + trustInfo.getTrustedPGPKeys().forEach(keyStore::addKey); + IStatus status = persistTrustedKeys(keyStore); + if (status != null && !status.isOK()) { + statuses.add(status); + } + } + + if (!statuses.isEmpty()) { + if (statuses.size() == 1) { + return statuses.get(1); + } + String message = statuses.stream().map(IStatus::getMessage).collect(Collectors.joining("\n")); //$NON-NLS-1$ + return new MultiStatus(getClass(), IStatus.OK, statuses.toArray(IStatus[]::new), message, null); + } } - // in case keys from artifact were not imported yet in keystore, add them - PGPSignatureVerifier.KNOWN_KEYS - .addKeys(artifact.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); - return PGPSignatureVerifier.KNOWN_KEYS.getKey(id); + + return Status.OK_STATUS; } - private IStatus persistTrustedCertificates(Certificate[] trustedCertificates) { - if (trustedCertificates == null) - // I'm pretty sure this would be a bug; trustedCertificates should never be null here. - return new Status(IStatus.INFO, EngineActivator.ID, Messages.CertificateChecker_CertificateRejected); - ServiceTracker<TrustEngine, TrustEngine> trustEngineTracker = new ServiceTracker<>(EngineActivator.getContext(), TrustEngine.class, null); + /** + * This modifies the argument collection to remove the certificates that were + * successfully saved. Often no certificates are saved because this tries to + * store in the Java runtime cacerts and those are typically read-only. + */ + private IStatus persistTrustedCertificatesInTrustEngine( + Collection<? extends Certificate> unsavedTrustedCertificates) { + ServiceTracker<TrustEngine, TrustEngine> trustEngineTracker = new ServiceTracker<>(EngineActivator.getContext(), + TrustEngine.class, null); trustEngineTracker.open(); Object[] trustEngines = trustEngineTracker.getServices(); try { if (trustEngines == null) return null; - for (Certificate trustedCertificate : trustedCertificates) { + for (Iterator<? extends Certificate> it = unsavedTrustedCertificates.iterator(); it.hasNext();) { + Certificate trustedCertificate = it.next(); for (Object engine : trustEngines) { TrustEngine trustEngine = (TrustEngine) engine; if (trustEngine.isReadOnly()) continue; try { trustEngine.addTrustAnchor(trustedCertificate, trustedCertificate.toString()); - // this should mean we added an anchor successfully; continue to next certificate + // this should mean we added an anchor successfully; continue to next + // certificate + it.remove(); break; } catch (IOException e) { - //just return an INFO so the user can proceed with the install - return new Status(IStatus.INFO, EngineActivator.ID, Messages.CertificateChecker_KeystoreConnectionError, e); + // just return an INFO so the user can proceed with the install + return new Status(IStatus.INFO, EngineActivator.ID, + Messages.CertificateChecker_KeystoreConnectionError, e); } catch (GeneralSecurityException e) { - return new Status(IStatus.INFO, EngineActivator.ID, Messages.CertificateChecker_CertificateError, e); + return new Status(IStatus.INFO, EngineActivator.ID, + Messages.CertificateChecker_CertificateError, e); } } } @@ -283,6 +380,43 @@ public class CertificateChecker { return Status.OK_STATUS; } + public IStatus persistTrustedCertificates(Collection<? extends Certificate> trustedCertificates) { + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + IEclipsePreferences node = profileScope.getNode(EngineActivator.ID); + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + CertPath certPath = certificateFactory.generateCertPath(new ArrayList<>(trustedCertificates)); + byte[] encoded = certPath.getEncoded("PKCS7"); //$NON-NLS-1$ + node.putByteArray(TRUSTED_CERTIFICATES_PROPERTY, encoded); + node.flush(); + } catch (BackingStoreException | CertificateException ex) { + return new Status(IStatus.ERROR, EngineActivator.ID, ex.getMessage(), ex); + } + } + return Status.OK_STATUS; + } + + public Set<? extends Certificate> getPreferenceTrustedCertificates() { + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + IEclipsePreferences node = profileScope.getNode(EngineActivator.ID); + try { + byte[] encoded = node.getByteArray(TRUSTED_CERTIFICATES_PROPERTY, null); + if (encoded != null) { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + CertPath certPath = certificateFactory.generateCertPath(new ByteArrayInputStream(encoded), "PKCS7"); //$NON-NLS-1$ + return new LinkedHashSet<>(certPath.getCertificates()); + } + } catch (CertificateException ex) { + DebugHelper.debug(DEBUG_PREFIX, ex.getMessage()); + } + } + return Set.of(); + } + /** * Return the policy on unsigned content. */ @@ -294,37 +428,87 @@ public class CertificateChecker { } + public void setProfile(IProfile profile) { + this.profile = profile; + } + public void add(Map<IArtifactDescriptor, File> toAdd) { artifacts.putAll(toAdd); } - public PGPPublicKeyStore buildPGPTrustore() { + public Map<PGPPublicKey, Set<Bundle>> getContributedTrustedKeys() { + // Build the map based on fingerprints to properly avoid duplicates as we + // accumulate the full set of keys. + Map<ByteBuffer, Set<Bundle>> keys = new LinkedHashMap<>(); + + // Load from the extension registry. + for (IConfigurationElement extension : RegistryFactory.getRegistry() + .getConfigurationElementsFor(EngineActivator.ID, "pgp")) { //$NON-NLS-1$ + if ("trustedKeys".equals(extension.getName())) { //$NON-NLS-1$ + String pathInBundle = extension.getAttribute("path"); //$NON-NLS-1$ + if (pathInBundle != null) { + String name = extension.getContributor().getName(); + Stream.of(EngineActivator.getContext().getBundles()) + .filter(bundle -> bundle.getSymbolicName().equals(name)).findAny().ifPresent(bundle -> { + URL keyURL = bundle.getEntry(pathInBundle); + try (InputStream stream = keyURL.openStream()) { + PGPPublicKeyStore.readPublicKeys(stream).stream().map(keyService::addKey) + .forEach(key -> keys.computeIfAbsent(ByteBuffer.wrap(key.getFingerprint()), + k -> new LinkedHashSet<>()).add(bundle)); + } catch (IOException e) { + DebugHelper.debug(DEBUG_PREFIX, e.getMessage()); + } + }); + } + } + } + + Map<PGPPublicKey, Set<Bundle>> result = keys.entrySet().stream() + .collect(Collectors.toMap(entry -> keyService.getKey(entry.getKey().array()), Map.Entry::getValue)); + return result; + } + + public boolean isTrustAlways() { + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + return profileScope.getNode(EngineActivator.ID).getBoolean(TRUST_ALWAYS_PROPERTY, false); + } + return false; + } + + public void setTrustAlways(boolean trustAlways) { + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + profileScope.getNode(EngineActivator.ID).putBoolean(TRUST_ALWAYS_PROPERTY, trustAlways); + } + } + + public PGPPublicKeyStore getPreferenceTrustedKeys() { PGPPublicKeyStore trustStore = new PGPPublicKeyStore(); - IProfile profile = agent.getService(IProfileRegistry.class).getProfile(IProfileRegistry.SELF); - trustStore.addKeys(profile.getProperty(TRUSTED_KEY_STORE_PROPERTY)); - ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), profile.getProfileId()); - trustStore.addKeys(profileScope.getNode(EngineActivator.ID).get(TRUSTED_KEY_STORE_PROPERTY, null)); - //// SECURITY ISSUE: next lines become an attack vector as we have no guarantee - //// the metadata of those IUs is safe/were signed. - //// https://bugs.eclipse.org/bugs/show_bug.cgi?id=576705#c4 - // profile.query(QueryUtil.ALL_UNITS, new NullProgressMonitor()).forEach( - // iu -> - // store.addAll(PGPSignatureVerifier.readPublicKeys(iu.getProperty(TRUSTED_KEY_STORE_PROPERTY)))); - trustStore.all().forEach(PGPSignatureVerifier.KNOWN_KEYS::addKey); + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + PGPPublicKeyStore + .readPublicKeys(profileScope.getNode(EngineActivator.ID).get(TRUSTED_KEY_STORE_PROPERTY, null)) + .stream().map(keyService::addKey).forEach(trustStore::addKey); + } return trustStore; } public IStatus persistTrustedKeys(PGPPublicKeyStore trustStore) { - IProfile profile = agent.getService(IProfileRegistry.class).getProfile(IProfileRegistry.SELF); - ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), profile.getProfileId()); - IEclipsePreferences node = profileScope.getNode(EngineActivator.ID); - try { - node.put(TRUSTED_KEY_STORE_PROPERTY, trustStore.toArmoredString()); - node.flush(); - return Status.OK_STATUS; - } catch (IOException | BackingStoreException ex) { - return new Status(IStatus.ERROR, EngineActivator.ID, ex.getMessage(), ex); + if (profile != null) { + ProfileScope profileScope = new ProfileScope(agent.getService(IAgentLocation.class), + profile.getProfileId()); + IEclipsePreferences node = profileScope.getNode(EngineActivator.ID); + try { + node.put(TRUSTED_KEY_STORE_PROPERTY, trustStore.toArmoredString()); + node.flush(); + } catch (IOException | BackingStoreException ex) { + return new Status(IStatus.ERROR, EngineActivator.ID, ex.getMessage(), ex); + } } + return Status.OK_STATUS; } } - diff --git a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CheckTrust.java b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CheckTrust.java index e885ad1f6..5b81e7ba3 100644 --- a/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CheckTrust.java +++ b/bundles/org.eclipse.equinox.p2.engine/src/org/eclipse/equinox/internal/p2/engine/phases/CheckTrust.java @@ -60,6 +60,7 @@ public class CheckTrust extends InstallableUnitPhase { // Instantiate a check trust manager CertificateChecker certificateChecker = new CertificateChecker(agent); certificateChecker.add(artifactRequests); + certificateChecker.setProfile(profile); return certificateChecker.start(); } diff --git a/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF index 76a2520ae..290732d27 100644 --- a/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF @@ -2,11 +2,11 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.extensionlocation;singleton:=true -Bundle-Version: 1.4.0.qualifier +Bundle-Version: 1.4.100.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.extensionlocation.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin -Export-Package: org.eclipse.equinox.internal.p2.extensionlocation;x-friends:="org.eclipse.equinox.p2.reconciler.dropins,org.eclipse.equinox.p2.ui.importexport" +Export-Package: org.eclipse.equinox.internal.p2.extensionlocation;x-friends:="org.eclipse.equinox.p2.reconciler.dropins,org.eclipse.equinox.p2.ui,org.eclipse.equinox.p2.ui.importexport" Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)", org.eclipse.equinox.p2.metadata Bundle-RequiredExecutionEnvironment: JavaSE-11 diff --git a/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml b/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml index 474ce108b..2cadcc0b2 100644 --- a/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml +++ b/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.extensionlocation</artifactId> - <version>1.4.0-SNAPSHOT</version> + <version>1.4.100-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.garbagecollector/pom.xml b/bundles/org.eclipse.equinox.p2.garbagecollector/pom.xml index 989378df4..ee0e4278b 100644 --- a/bundles/org.eclipse.equinox.p2.garbagecollector/pom.xml +++ b/bundles/org.eclipse.equinox.p2.garbagecollector/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.installer/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.installer/META-INF/MANIFEST.MF index d1be7dc49..63f6c3707 100644 --- a/bundles/org.eclipse.equinox.p2.installer/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.installer/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-SymbolicName: org.eclipse.equinox.p2.installer;singleton:=true Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin -Bundle-Version: 1.3.200.qualifier +Bundle-Version: 1.3.300.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.installer.InstallerActivator Bundle-ActivationPolicy: lazy Require-Bundle: org.eclipse.osgi;bundle-version="[3.7.0,4.0.0)", diff --git a/bundles/org.eclipse.equinox.p2.installer/installer.product b/bundles/org.eclipse.equinox.p2.installer/installer.product index 6c2bd7198..83b557986 100644 --- a/bundles/org.eclipse.equinox.p2.installer/installer.product +++ b/bundles/org.eclipse.equinox.p2.installer/installer.product @@ -40,7 +40,7 @@ <plugin id="org.eclipse.core.expressions"/> <plugin id="org.eclipse.core.jobs"/> <plugin id="org.eclipse.core.net"/> - <plugin id="org.eclipse.core.net.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.core.net.linux" fragment="true"/> <plugin id="org.eclipse.core.runtime"/> <plugin id="org.eclipse.core.runtime.compatibility.registry" fragment="true"/> <plugin id="org.eclipse.ecf"/> @@ -76,7 +76,7 @@ <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.security.macosx" fragment="true"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> <plugin id="org.eclipse.equinox.simpleconfigurator.manipulator"/> diff --git a/bundles/org.eclipse.equinox.p2.installer/installer.properties b/bundles/org.eclipse.equinox.p2.installer/installer.properties index c28de9f82..ef00a4347 100644 --- a/bundles/org.eclipse.equinox.p2.installer/installer.properties +++ b/bundles/org.eclipse.equinox.p2.installer/installer.properties @@ -11,8 +11,8 @@ # Contributors: # IBM Corporation - initial API and implementation ############################################################################### -eclipse.p2.metadata=http://download.eclipse.org/eclipse/updates/3.5 -eclipse.p2.artifacts=http://download.eclipse.org/eclipse/updates/3.5 +eclipse.p2.metadata=https://download.eclipse.org/eclipse/updates/3.5 +eclipse.p2.artifacts=https://download.eclipse.org/eclipse/updates/3.5 eclipse.p2.flavor=tooling eclipse.p2.profileName=Eclipse SDK eclipse.p2.launcherName=eclipse diff --git a/bundles/org.eclipse.equinox.p2.installer/pom.xml b/bundles/org.eclipse.equinox.p2.installer/pom.xml index 2e06dd169..27ae673d5 100644 --- a/bundles/org.eclipse.equinox.p2.installer/pom.xml +++ b/bundles/org.eclipse.equinox.p2.installer/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.installer</artifactId> - <version>1.3.200-SNAPSHOT</version> + <version>1.3.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF index 2257e8ff3..283bf2bb4 100644 --- a/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-SymbolicName: org.eclipse.equinox.p2.jarprocessor;singleton:=true Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin -Bundle-Version: 1.2.100.qualifier +Bundle-Version: 1.2.300.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 Main-Class: org.eclipse.equinox.internal.p2.jarprocessor.Main Export-Package: org.eclipse.equinox.internal.p2.jarprocessor;x-friends:="org.eclipse.equinox.p2.artifact.repository,org.eclipse.pde.build", diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/forceQualifierUpdate.txt b/bundles/org.eclipse.equinox.p2.jarprocessor/forceQualifierUpdate.txt index 88375d023..1519a409d 100644 --- a/bundles/org.eclipse.equinox.p2.jarprocessor/forceQualifierUpdate.txt +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/forceQualifierUpdate.txt @@ -6,4 +6,6 @@ comparator errors on new infra Bug 566668 - Comparator errors in I20200904-0210 Bug 571555 - Comparator errors in I20210226-1800 Bug 571731 - Comparator errors in I20210305-0820 -Bug 575841 - Comparator errors in I20210906-1000
\ No newline at end of file +Bug 575841 - Comparator errors in I20210906-1000 +Bug 578351 - Lambda generation order is unstable in ecj +Bug 579126 - 4.24 I-Build: I20220307-0340 - Comparator Errors Found
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.jarprocessor/pom.xml b/bundles/org.eclipse.equinox.p2.jarprocessor/pom.xml index caf173f63..7064c875d 100644 --- a/bundles/org.eclipse.equinox.p2.jarprocessor/pom.xml +++ b/bundles/org.eclipse.equinox.p2.jarprocessor/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.jarprocessor</artifactId> - <version>1.2.100-SNAPSHOT</version> + <version>1.2.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/pom.xml b/bundles/org.eclipse.equinox.p2.metadata.repository/pom.xml index afd2687b0..4058eb8f1 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/pom.xml +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/pom.xml @@ -14,7 +14,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.metadata/pom.xml b/bundles/org.eclipse.equinox.p2.metadata/pom.xml index eb0923e93..bf0807827 100644 --- a/bundles/org.eclipse.equinox.p2.metadata/pom.xml +++ b/bundles/org.eclipse.equinox.p2.metadata/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.operations/pom.xml b/bundles/org.eclipse.equinox.p2.operations/pom.xml index 621d99d8a..ed63d9000 100644 --- a/bundles/org.eclipse.equinox.p2.operations/pom.xml +++ b/bundles/org.eclipse.equinox.p2.operations/pom.xml @@ -14,7 +14,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/org.eclipse.jdt.ui.prefs index 708515333..d3d075e63 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/org.eclipse.jdt.ui.prefs +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/.settings/org.eclipse.jdt.ui.prefs @@ -23,7 +23,7 @@ sp_cleanup.always_use_this_for_non_static_method_access=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true -sp_cleanup.format_source_code_changes_only=false +sp_cleanup.format_source_code_changes_only=true sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF index 049dcc704..0a8d62359 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.publisher.eclipse;singleton:=true -Bundle-Version: 1.4.1.qualifier +Bundle-Version: 1.4.2.qualifier Bundle-Activator: org.eclipse.pde.internal.publishing.Activator Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/pom.xml b/bundles/org.eclipse.equinox.p2.publisher.eclipse/pom.xml index 9068e97a1..cf46b80e9 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/pom.xml +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/pom.xml @@ -4,12 +4,12 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.publisher.eclipse</artifactId> - <version>1.4.1-SNAPSHOT</version> + <version>1.4.2-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/FeaturesAction.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/FeaturesAction.java index cbc41b8c6..d11aaa39d 100644 --- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/FeaturesAction.java +++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/FeaturesAction.java @@ -614,7 +614,14 @@ public class FeaturesAction extends AbstractPublisherAction { // artifact per feature IU. Collection<IArtifactKey> artifacts = featureIU.getArtifacts(); for (IArtifactKey artifactKey : artifacts) { - File file = new File(feature.getLocation()); + String location = feature.getLocation(); + if (location == null) { + if (PublisherHelper.isArtifactPublish(publisherInfo)) { + throw new IllegalArgumentException(NLS.bind(Messages.exception_sourcePath, feature.getId())); + } + continue; + } + File file = new File(location); ArtifactDescriptor ad = (ArtifactDescriptor) PublisherHelper.createArtifactDescriptor(info, artifactKey, file); processArtifactPropertiesAdvice(featureIU, ad, publisherInfo); ad.setProperty(IArtifactDescriptor.DOWNLOAD_CONTENTTYPE, IArtifactDescriptor.TYPE_ZIP); diff --git a/bundles/org.eclipse.equinox.p2.publisher/.settings/.api_filters b/bundles/org.eclipse.equinox.p2.publisher/.settings/.api_filters new file mode 100644 index 000000000..5fbae6834 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.publisher/.settings/.api_filters @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.equinox.p2.publisher" version="2"> + <resource path="META-INF/MANIFEST.MF"> + <filter id="926941240"> + <message_arguments> + <message_argument value="1.7.0"/> + <message_argument value="1.6.200"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/bundles/org.eclipse.equinox.p2.publisher/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.publisher/META-INF/MANIFEST.MF index 2568eeee2..693d9e66b 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.publisher/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.publisher;singleton:=true -Bundle-Version: 1.6.200.qualifier +Bundle-Version: 1.7.0.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.publisher.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.equinox.p2.publisher/pom.xml b/bundles/org.eclipse.equinox.p2.publisher/pom.xml index aa9c72cd8..b16ba1bd1 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/pom.xml +++ b/bundles/org.eclipse.equinox.p2.publisher/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.publisher</artifactId> - <version>1.6.200-SNAPSHOT</version> + <version>1.7.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java index 497a8a261..5f1dc9d42 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java +++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/AbstractPublisherAction.java @@ -504,7 +504,7 @@ public abstract class AbstractPublisherAction implements IPublisherAction { // if all we are doing is indexing things then add the descriptor and get on // with it - if ((publisherInfo.getArtifactOptions() & IPublisherInfo.A_PUBLISH) == 0) { + if (!PublisherHelper.isArtifactPublish(publisherInfo)) { destination.addDescriptor(descriptor, new NullProgressMonitor()); return; } @@ -556,7 +556,7 @@ public abstract class AbstractPublisherAction implements IPublisherAction { return; // if all we are doing is indexing things then add the descriptor and get on // with it - if ((publisherInfo.getArtifactOptions() & IPublisherInfo.A_PUBLISH) == 0) { + if (!PublisherHelper.isArtifactPublish(publisherInfo)) { destination.addDescriptor(descriptor, new NullProgressMonitor()); return; } diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/IPublisherInfo.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/IPublisherInfo.java index 0e88610aa..33796f507 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/IPublisherInfo.java +++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/p2/publisher/IPublisherInfo.java @@ -8,8 +8,8 @@ * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: + * + * Contributors: * Code 9 - initial API and implementation * IBM - ongoing development ******************************************************************************/ @@ -40,7 +40,7 @@ public interface IPublisherInfo { public static final int A_OVERWRITE = 4; /** - * A bitwise flag indicating that MD5 hash should not be generated when + * A bitwise flag indicating that MD5 hash should not be generated when * publishing an artifact. When this flag is not specified the MD5 hash will * be generated by default. */ @@ -61,9 +61,9 @@ public interface IPublisherInfo { public IMetadataRepository getMetadataRepository(); /** - * Returns whether or not artifacts themselves should be published. - * @return <code>true</code> if artifacts should be published. - * <code>false</code> otherwise. + * Returns a bitflag that controls the publish operation, what is a bitwhise OR + * of {@link #A_INDEX},{@value #A_PUBLISH}, {@link #A_NO_MD5}, + * {@link #A_OVERWRITE} or <code>0</code> if no flags are set. */ public int getArtifactOptions(); @@ -80,7 +80,7 @@ public interface IPublisherInfo { public <T extends IPublisherAdvice> Collection<T> getAdvice(String configSpec, boolean includeDefault, String id, Version version, Class<T> type); /** - * Add the given advice to the set of publishing advices. + * Add the given advice to the set of publishing advices. * @param advice the advice to retain */ public void addAdvice(IPublisherAdvice advice); @@ -95,19 +95,19 @@ public interface IPublisherInfo { public String[] getConfigurations(); /** - * Returns the artifact repository given to the publisher as context for - * the publisher actions. May be <code>null</code>. Note that multiple + * Returns the artifact repository given to the publisher as context for + * the publisher actions. May be <code>null</code>. Note that multiple * repositories may be represented as one composite repository. - * + * * @return the context artifact repository or <code>null</code> if none. */ public IArtifactRepository getContextArtifactRepository(); /** - * Returns the metadata repository given to the publisher as context for - * the publisher actions. May be <code>null</code>. Note that multiple + * Returns the metadata repository given to the publisher as context for + * the publisher actions. May be <code>null</code>. Note that multiple * repositories may be represented as one composite repository. - * + * * @return the context metadata repository or <code>null</code> if none. */ public IMetadataRepository getContextMetadataRepository(); diff --git a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/spi/p2/publisher/PublisherHelper.java b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/spi/p2/publisher/PublisherHelper.java index d62b50b90..f074794b7 100644 --- a/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/spi/p2/publisher/PublisherHelper.java +++ b/bundles/org.eclipse.equinox.p2.publisher/src/org/eclipse/equinox/spi/p2/publisher/PublisherHelper.java @@ -112,7 +112,7 @@ public class PublisherHelper { descriptor.setProperty(IArtifactDescriptor.ARTIFACT_SIZE, Long.toString(pathOnDisk.length())); descriptor.setProperty(IArtifactDescriptor.DOWNLOAD_SIZE, Long.toString(pathOnDisk.length())); - boolean generateChecksums = info == null || (info.getArtifactOptions() & IPublisherInfo.A_NO_MD5) == 0; + boolean generateChecksums = info == null || isArtifactGenerateChecksums(info); if (generateChecksums) { calculateChecksums(pathOnDisk, descriptor); } @@ -252,4 +252,49 @@ public class PublisherHelper { return new VersionRange(min, includeMin, max, includeMax); } + + /** + * + * @return <code>true</code> if md5 sums should be generated <code>false</code> + * otherwise + * @since 1.7.0 + */ + public static boolean isArtifactGenerateChecksums(IPublisherInfo publisherInfo) { + if ((publisherInfo.getArtifactOptions() & IPublisherInfo.A_NO_MD5) != 0) { + return false; + } + return true; + } + + /** + * + * @return <code>true</code> if existing artifacts should be overwritten + * <code>false</code> otherwise + * @since 1.7.0 + */ + public static boolean isArtifactOverwrite(IPublisherInfo publisherInfo) { + return (publisherInfo.getArtifactOptions() & IPublisherInfo.A_OVERWRITE) != 0; + } + + /** + * + * @return <code>true</code> if artifacts should published <code>false</code> + * otherwise + * @since 1.7.0 + */ + public static boolean isArtifactPublish(IPublisherInfo publisherInfo) { + int artifactOptions = publisherInfo.getArtifactOptions(); + return artifactOptions == 0 || (artifactOptions & IPublisherInfo.A_PUBLISH) != 0; + } + + /** + * + * @return <code>true</code> if the artifact index should be updated + * <code>false</code> otherwise + * @since 1.7.0 + */ + public static boolean isArtifactIndex(IPublisherInfo publisherInfo) { + int artifactOptions = publisherInfo.getArtifactOptions(); + return artifactOptions == 0 || (artifactOptions & IPublisherInfo.A_INDEX) != 0; + } } diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/Bootstrap.product b/bundles/org.eclipse.equinox.p2.reconciler.dropins/Bootstrap.product index 833e2413c..f48bab7f0 100644 --- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/Bootstrap.product +++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/Bootstrap.product @@ -30,7 +30,7 @@ <plugin id="org.eclipse.core.expressions"/> <plugin id="org.eclipse.core.jobs"/> <plugin id="org.eclipse.core.net"/> - <plugin id="org.eclipse.core.net.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.core.net.linux" fragment="true"/> <plugin id="org.eclipse.core.runtime"/> <plugin id="org.eclipse.core.runtime.compatibility.registry" fragment="true"/> <plugin id="org.eclipse.ecf"/> @@ -64,7 +64,7 @@ <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> <plugin id="org.eclipse.equinox.simpleconfigurator.manipulator"/> <plugin id="org.eclipse.equinox.util"/> diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.reconciler.dropins/META-INF/MANIFEST.MF index 5131cf0e6..104fdb8d6 100644 --- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.reconciler.dropins;singleton:=true -Bundle-Version: 1.4.0.qualifier +Bundle-Version: 1.4.100.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.reconciler.dropins.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.equinox.p2.reconciler.dropins/pom.xml b/bundles/org.eclipse.equinox.p2.reconciler.dropins/pom.xml index bc21ce7f7..d2362a506 100644 --- a/bundles/org.eclipse.equinox.p2.reconciler.dropins/pom.xml +++ b/bundles/org.eclipse.equinox.p2.reconciler.dropins/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.reconciler.dropins</artifactId> - <version>1.4.0-SNAPSHOT</version> + <version>1.4.100-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.repository.tools/pom.xml b/bundles/org.eclipse.equinox.p2.repository.tools/pom.xml index 2eb240c5e..379a11b8a 100644 --- a/bundles/org.eclipse.equinox.p2.repository.tools/pom.xml +++ b/bundles/org.eclipse.equinox.p2.repository.tools/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.repository/.options b/bundles/org.eclipse.equinox.p2.repository/.options new file mode 100644 index 000000000..c2d3dc538 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/.options @@ -0,0 +1,3 @@ +org.eclipse.equinox.p2.repository/credentials/debug = false +org.eclipse.equinox.p2.repository/transport/debug = false +org.eclipse.equinox.p2.repository/keyservice/debug = false diff --git a/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF index ce71e8628..0f0800872 100644 --- a/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.repository/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.repository;singleton:=true -Bundle-Version: 2.5.300.qualifier +Bundle-Version: 2.6.0.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.repository.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -42,6 +42,16 @@ Require-Bundle: org.eclipse.equinox.common, Bundle-RequiredExecutionEnvironment: JavaSE-11 Bundle-ActivationPolicy: lazy Import-Package: javax.crypto, + org.bouncycastle;version="1.69.0", + org.bouncycastle.bcpg;version="1.69.0", + org.bouncycastle.gpg.keybox;version="1.69.0", + org.bouncycastle.gpg.keybox.jcajce;version="1.69.0", + org.bouncycastle.openpgp;version="1.69.0", + org.bouncycastle.openpgp.jcajce;version="1.69.0", + org.bouncycastle.openpgp.operator;version="1.69.0", + org.bouncycastle.openpgp.operator.bc;version="1.69.0", + org.bouncycastle.openpgp.operator.jcajce;version="1.69.0", + org.bouncycastle.util.encoders;version="1.69.0", org.eclipse.core.runtime.jobs, org.eclipse.core.runtime.preferences;version="3.2.0", org.eclipse.equinox.internal.p2.core, @@ -61,5 +71,5 @@ Import-Package: javax.crypto, org.osgi.service.packageadmin;version="1.2.0", org.osgi.service.prefs;version="1.0.0", org.osgi.util.tracker;version="1.4.0" -Service-Component: OSGI-INF/cacheManager.xml +Service-Component: OSGI-INF/cacheManager.xml, OSGI-INF/pgpPublicKeyService.xml Automatic-Module-Name: org.eclipse.equinox.p2.repository diff --git a/bundles/org.eclipse.equinox.p2.repository/OSGI-INF/pgpPublicKeyService.xml b/bundles/org.eclipse.equinox.p2.repository/OSGI-INF/pgpPublicKeyService.xml new file mode 100644 index 000000000..e303cd93e --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/OSGI-INF/pgpPublicKeyService.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipse.equinox.p2.repository.spi.pgpPublicKeyService"> + <implementation class="org.eclipse.equinox.internal.p2.repository.PGPKeyServiceComponent"/> + <service> + <provide interface="org.eclipse.equinox.p2.core.spi.IAgentServiceFactory"/> + </service> + <property name="p2.agent.servicename" type="String" value="org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService"/> +</scr:component> diff --git a/bundles/org.eclipse.equinox.p2.repository/build.properties b/bundles/org.eclipse.equinox.p2.repository/build.properties index f3b28f666..89e396d06 100644 --- a/bundles/org.eclipse.equinox.p2.repository/build.properties +++ b/bundles/org.eclipse.equinox.p2.repository/build.properties @@ -17,6 +17,7 @@ bin.includes = META-INF/,\ about.html,\ plugin.properties,\ OSGI-INF/cacheManager.xml,\ - OSGI-INF/ + OSGI-INF/,\ + .options src.includes = about.html source.. = src/ diff --git a/bundles/org.eclipse.equinox.p2.repository/pom.xml b/bundles/org.eclipse.equinox.p2.repository/pom.xml index f80aa9c5c..7b16ab7c0 100644 --- a/bundles/org.eclipse.equinox.p2.repository/pom.xml +++ b/bundles/org.eclipse.equinox.p2.repository/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.repository</artifactId> - <version>2.5.300-SNAPSHOT</version> + <version>2.6.0-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/PGPKeyServiceComponent.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/PGPKeyServiceComponent.java new file mode 100644 index 000000000..dfb73bc90 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/PGPKeyServiceComponent.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.repository; + +import org.eclipse.equinox.internal.provisional.p2.repository.DefaultPGPPublicKeyService; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory; + +public class PGPKeyServiceComponent implements IAgentServiceFactory { + @Override + public Object createService(IProvisioningAgent agent) { + return new DefaultPGPPublicKeyService(agent); + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java index bb422888a..e4955cefe 100644 --- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/DebugHelper.java @@ -25,15 +25,18 @@ public class DebugHelper { public static final boolean DEBUG_REPOSITORY_CREDENTIALS; public static final boolean DEBUG_REPOSITORY_TRANSPORT; + public static final boolean DEBUG_KEY_SERVICE; static { DebugOptions options = ServiceHelper.getService(Activator.getContext(), DebugOptions.class); if (options != null) { DEBUG_REPOSITORY_CREDENTIALS = options.getBooleanOption(Activator.ID + "/credentials/debug", false); //$NON-NLS-1$ DEBUG_REPOSITORY_TRANSPORT = options.getBooleanOption(Activator.ID + "/transport/debug", false); //$NON-NLS-1$ + DEBUG_KEY_SERVICE = options.getBooleanOption(Activator.ID + "/keyservice/debug", false); //$NON-NLS-1$ } else { DEBUG_REPOSITORY_CREDENTIALS = false; DEBUG_REPOSITORY_TRANSPORT = false; + DEBUG_KEY_SERVICE = false; } } @@ -50,7 +53,7 @@ public class DebugHelper { System.out.println(buffer.toString()); } - public static void debug(String name, String message, Object[] keyValueArray) { + public static void debug(String name, String message, Object... keyValueArray) { if (keyValueArray == null || keyValueArray.length == 0) debug(name, message); else { diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/DefaultPGPPublicKeyService.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/DefaultPGPPublicKeyService.java new file mode 100644 index 000000000..3aa501986 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/provisional/p2/repository/DefaultPGPPublicKeyService.java @@ -0,0 +1,869 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.internal.provisional.p2.repository; + +import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.ByteBuffer; +import java.nio.file.*; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.util.*; +import java.util.function.*; +import org.bouncycastle.bcpg.ArmoredInputStream; +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.gpg.keybox.*; +import org.bouncycastle.gpg.keybox.jcajce.JcaKeyBoxBuilder; +import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; +import org.eclipse.equinox.internal.p2.repository.Transport; +import org.eclipse.equinox.internal.p2.repository.helpers.DebugHelper; +import org.eclipse.equinox.p2.core.IAgentLocation; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; + +/** + * @since 2.6 + */ +public class DefaultPGPPublicKeyService extends PGPPublicKeyService { + + /** + * Enable debug tracing either via debug options or via a system property. + */ + private static final boolean DEBUG_KEY_SERVICE = DebugHelper.DEBUG_KEY_SERVICE + || Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty("p2.keyserver.debug")); //$NON-NLS-1$ + + /** + * The system property used to initialized the {@link #keyServer}. + */ + private static final String KEY_SERVERS_PROPERTY = "p2.keyservers"; //$NON-NLS-1$ + + /** + * The system property used to determine where to look for the GPG pubring. + * + * @see #getGPPDirectory() + */ + private static final String GPG_HOME_PROPERTY = "p2.gpg.home"; //$NON-NLS-1$ + + /** + * The system property used to determine whether to enable GPG pubring lookup. + * + * @see #gpg + * @see #setGPG(boolean) + */ + private static final String GPG_PROPERTY = "p2.gpg"; //$NON-NLS-1$ + + /** + * The number of elapsed milliseconds after which keys cached from a key server + * are considered stale such that they will be re-fetched if possible. + */ + private static final long STALE_AFTER_MILLIS = Long.getLong("p2.keyserver.cache.stale", 24) * 1000 * 60 * 60; //$NON-NLS-1$ + + /** + * Reuse p2's transport layer for fetching keys from the key server. + */ + private final Transport transport; + + /** + * Keys {@link #addKey(PGPPublicKey) added} to this key service are cached via + * this map. + */ + private final Map<Long, LocalKeyCache> localKeys = new LinkedHashMap<>(); + + /** + * A folder with locally cached keys, indexed on {@link PGPPublicKey#getKeyID() + * key ID}. + */ + private final Path keyCache; + + /** + * The current key servers. + */ + private final Map<String, PGPKeyServer> keyServers = new LinkedHashMap<>(); + + /** + * Whether to load from GPG's pubring. + */ + private boolean gpg; + + /** + * Creates an instance associated with the given agent. + * + * @param agent the agent for which a key service is provided. + */ + public DefaultPGPPublicKeyService(IProvisioningAgent agent) { + IAgentLocation agentLocation = agent.getService(IAgentLocation.class); + URI dataArea = agentLocation.getDataArea(org.eclipse.equinox.internal.p2.repository.Activator.ID); + keyCache = Paths.get(dataArea).resolve("pgp"); //$NON-NLS-1$ + try { + Files.createDirectories(keyCache); + } catch (IOException e) { + throw new RuntimeException(e); + } + + if (DEBUG_KEY_SERVICE) { + DebugHelper.debug("KeyServer", "Cache", "location", keyCache); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + String keyServersProperty = System.getProperty(KEY_SERVERS_PROPERTY, ""); //$NON-NLS-1$ + if (!keyServersProperty.isBlank()) { + Set<String> keyServersSet = new LinkedHashSet<>(); + for (String keyServer : keyServersProperty.split("[,; \t]+")) { //$NON-NLS-1$ + if (!keyServer.isEmpty()) { + keyServersSet.add(keyServer); + } + } + + setKeyServers(keyServersSet); + } + + setGPG(Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty(GPG_PROPERTY, Boolean.TRUE.toString())) + || !System.getProperty(GPG_HOME_PROPERTY, "").isBlank()); //$NON-NLS-1$ + + transport = agent.getService(Transport.class); + } + + public Set<String> getKeyServers() { + return Collections.unmodifiableSet(keyServers.keySet()); + } + + public void setKeyServers(Set<String> keyServers) { + Map<String, PGPKeyServer> newKeyServers = new LinkedHashMap<>(); + for (String keyServer : keyServers) { + PGPKeyServer pgpKeyServer = this.keyServers.get(keyServer); + if (pgpKeyServer == null) { + pgpKeyServer = new PGPKeyServer(keyServer, this.keyCache) { + @Override + protected boolean isStale(Path path) { + return DefaultPGPPublicKeyService.this.isStale(path); + } + + @Override + protected IStatus download(URI uri, OutputStream receiver, IProgressMonitor monitor) { + return DefaultPGPPublicKeyService.this.download(uri, receiver, monitor); + } + + @Override + protected void log(Throwable throwable) { + DefaultPGPPublicKeyService.this.log(throwable); + } + }; + } + newKeyServers.put(keyServer, pgpKeyServer); + } + + this.keyServers.clear(); + this.keyServers.putAll(newKeyServers); + } + + @Override + public PGPPublicKey getKey(String fingerprint) { + int length = fingerprint.length(); + if (length >= 16) { + long keyID = Long.parseUnsignedLong(fingerprint.substring(length - 16, length), 16); + Collection<PGPPublicKey> keys = getKeys(keyID); + for (PGPPublicKey key : keys) { + if (toHexFingerprint(key).equalsIgnoreCase(fingerprint)) { + return key; + } + } + } + return null; + } + + @Override + public Collection<PGPPublicKey> getKeys(long keyID) { + List<PGPPublicKey> keys = new ArrayList<>(); + for (PGPKeyServer keyServer : keyServers.values()) { + keys.addAll(keyServer.getKeys(keyID)); + } + + keys.addAll(getLocalKeyCache(keyID).get()); + + keys.addAll(getDefaultKeys(keyID)); + + return reconcileKeys(keys); + } + + public boolean isGGP() { + return gpg; + } + + public void setGPG(boolean gpg) { + this.gpg = gpg; + } + + protected List<PGPPublicKey> getDefaultKeys(long keyID) { + return gpg ? getGPGPubringKeys(keyID) : Collections.emptyList(); + } + + protected List<PGPPublicKey> reconcileKeys(List<PGPPublicKey> keys) { + if (keys.size() <= 1) { + return new ArrayList<>(keys); + } + + Map<ByteBuffer, PGPPublicKey> encodings = new LinkedHashMap<>(); + Map<ByteBuffer, PGPPublicKey> fingerprints = new LinkedHashMap<>(); + for (PGPPublicKey key : keys) { + try { + ByteBuffer encoding = ByteBuffer.wrap(key.getEncoded()); + PGPPublicKey existingKey = encodings.put(encoding, key); + if (existingKey == null) { + ByteBuffer fingerprint = ByteBuffer.wrap(key.getFingerprint()); + PGPPublicKey otherKey = fingerprints.put(fingerprint, key); + if (otherKey != null) { + fingerprints.put(fingerprint, choose(otherKey, key)); + } + } + } catch (IOException e) { + log(e); + } + } + + return new ArrayList<>(fingerprints.values()); + } + + /** + * While {@link #reconcileKeys(List) reconciling}, when two keys have the same + * fingerprint, this method must be chosen in favor of the other to be retained + * in the result. + * + * @param key1 the first key from which to choose. + * @param key2 the second key from which to choose. + * @return the key with the newest or most complete details. + */ + protected PGPPublicKey choose(PGPPublicKey key1, PGPPublicKey key2) { + // Favor the one with the newest information. + long signatureTime1 = getNewestSignature(key1); + long signatureTime2 = getNewestSignature(key2); + if (signatureTime1 > signatureTime2) { + return key1; + } else if (signatureTime1 < signatureTime2) { + return key2; + } + + // Favor the one with the most information. + int signatureCount1 = getSignatureCount(key1); + int signatureCount2 = getSignatureCount(key2); + if (signatureCount1 > signatureCount2) { + return key1; + } else if (signatureCount1 < signatureCount2) { + return key2; + } + + return key1; + } + + protected static int getSignatureCount(PGPPublicKey key) { + int result = 0; + for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext(); signatures.next()) { + ++result; + } + for (Iterator<PGPSignature> signatures = key.getKeySignatures(); signatures.hasNext(); signatures.next()) { + ++result; + } + return result; + } + + protected static long getNewestSignature(PGPPublicKey key) { + long result = 0; + for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long time = signature.getCreationTime().getTime(); + result = Math.max(result, time); + } + for (Iterator<PGPSignature> signatures = key.getKeySignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long time = signature.getCreationTime().getTime(); + result = Math.max(result, time); + } + + return result; + } + + @Override + public PGPPublicKey addKey(PGPPublicKey key) { + long keyID = key.getKeyID(); + LocalKeyCache localKeyCache = getLocalKeyCache(keyID); + localKeyCache.add(key); + + Collection<PGPPublicKey> keys = getKeys(keyID); + byte[] fingerprint = key.getFingerprint(); + for (PGPPublicKey otherKey : keys) { + if (Arrays.equals(otherKey.getFingerprint(), fingerprint)) { + return otherKey; + } + } + + // We should never get this far. + return key; + } + + protected boolean isStale(Path path) { + try { + FileTime lastModifiedTime = Files.getLastModifiedTime(path); + long lastModified = lastModifiedTime.toMillis(); + long currentTime = System.currentTimeMillis(); + return currentTime - lastModified > STALE_AFTER_MILLIS; + } catch (IOException e) { + return true; + } + } + + @Override + public Set<PGPPublicKey> getVerifiedCertifications(PGPPublicKey key) { + Set<PGPPublicKey> certifications = new LinkedHashSet<>(); + LOOP: for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long signingKeyID = signature.getKeyID(); + for (PGPPublicKey signingKey : getKeys(signingKeyID)) { + switch (signature.getSignatureType()) { + case PGPSignature.SUBKEY_BINDING: + case PGPSignature.PRIMARYKEY_BINDING: { + try { + signature.init(new BcPGPContentVerifierBuilderProvider(), signingKey); + if (signature.verifyCertification(signingKey, key) + && isCreatedBeforeRevocation(signature, signingKey)) { + certifications.add(signingKey); + continue LOOP; + } + } catch (PGPException e) { + //$FALL-THROUGH$ + } + break; + } + case PGPSignature.DEFAULT_CERTIFICATION: + case PGPSignature.NO_CERTIFICATION: + case PGPSignature.CASUAL_CERTIFICATION: + case PGPSignature.POSITIVE_CERTIFICATION: { + for (Iterator<String> userIDs = key.getUserIDs(); userIDs.hasNext();) { + String userID = userIDs.next(); + try { + signature.init(new BcPGPContentVerifierBuilderProvider(), signingKey); + if (signature.verifyCertification(userID, key) + && isCreatedBeforeRevocation(signature, signingKey)) { + certifications.add(signingKey); + continue LOOP; + } + } catch (PGPException e) { + //$FALL-THROUGH$ + } + } + break; + } + } + } + } + return certifications; + } + + @Override + public Date getVerifiedRevocationDate(PGPPublicKey key) { + for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long signingKeyID = signature.getKeyID(); + for (PGPPublicKey signingKey : getKeys(signingKeyID)) { + switch (signature.getSignatureType()) { + case PGPSignature.KEY_REVOCATION: + case PGPSignature.CERTIFICATION_REVOCATION: { + try { + signature.init(new BcPGPContentVerifierBuilderProvider(), signingKey); + if (signature.verifyCertification(key)) { + return signature.getCreationTime(); + } + } catch (PGPException e) { + //$FALL-THROUGH$ + } + break; + } + } + } + } + return null; + } + + private LocalKeyCache getLocalKeyCache(long keyID) { + LocalKeyCache localKeyCache = localKeys.get(keyID); + if (localKeyCache == null) { + String hexKeyID = toHex(keyID); + Path cache = keyCache.resolve(hexKeyID + ".asc"); //$NON-NLS-1$ + localKeyCache = new LocalKeyCache(cache) { + @Override + protected List<PGPPublicKey> reconcileKeys(List<PGPPublicKey> keys) { + return DefaultPGPPublicKeyService.this.reconcileKeys(keys); + } + + @Override + protected void log(Throwable throwable) { + DefaultPGPPublicKeyService.this.log(throwable); + } + }; + localKeys.put(keyID, localKeyCache); + } + return localKeyCache; + } + + protected Collection<PGPPublicKey> fetchKeys(URI uri, Path cache) throws IOException { + try { + ByteArrayOutputStream reciever = new ByteArrayOutputStream(); + IStatus download = download(uri, reciever, new NullProgressMonitor()); + if (!download.isOK()) { + Throwable exception = download.getException(); + if (exception != null) { + throw new IOException(download.getMessage(), exception); + } + throw new IOException(download.getMessage()); + } + List<PGPPublicKey> result = new ArrayList<>(); + byte[] bytes = reciever.toByteArray(); + try (InputStream input = new ArmoredInputStream(new ByteArrayInputStream(bytes))) { + result.addAll(loadKeys(input)); + } + + try (OutputStream out = newAtomicOutputStream(cache)) { + out.write(bytes); + } + + return result; + } catch (IOException ex) { + if (Files.isRegularFile(cache)) { + try (InputStream input = new ArmoredInputStream(new BufferedInputStream(Files.newInputStream(cache)))) { + return loadKeys(input); + } catch (IOException ex1) { + try { + // Assume the cache is corrupt so delete it. + Files.delete(cache); + } catch (IOException ex2) { + log(ex2); + } + // Rethrow original network failure exception + throw new IOException("Error while processing " + uri + " as well while processing the cache " //$NON-NLS-1$ //$NON-NLS-2$ + + cache + ": " + ex1.getMessage(), ex); //$NON-NLS-1$ + } + } + throw new IOException("Error while processing " + uri, ex); //$NON-NLS-1$ + } + } + + protected IStatus download(URI uri, OutputStream receiver, IProgressMonitor monitor) { + return transport.download(uri, receiver, monitor); + } + + protected void log(Throwable throwable) { + if (DEBUG_KEY_SERVICE) { + LogHelper.log(new Status(IStatus.ERROR, org.eclipse.equinox.internal.p2.repository.Activator.ID, + throwable.getMessage(), throwable)); + } + } + + protected static OutputStream newAtomicOutputStream(Path cache) throws IOException { + Path temp = Files.createTempFile(cache.getParent(), "out", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ + return new BufferedOutputStream(Files.newOutputStream(temp)) { + @Override + public void close() throws IOException { + super.close(); + Files.move(temp, cache, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); + } + }; + } + + protected static List<PGPPublicKey> loadKeys(InputStream input) throws IOException { + try { + List<PGPPublicKey> result = new ArrayList<>(); + for (Object o : new JcaPGPObjectFactory(input)) { + if (o instanceof PGPPublicKeyRingCollection) { + collectKeys((PGPPublicKeyRingCollection) o, result::add); + } else if (o instanceof PGPPublicKeyRing) { + collectKeys((PGPPublicKeyRing) o, result::add); + } else if (o instanceof PGPPublicKey) { + result.add((PGPPublicKey) o); + } + } + return result; + } catch (RuntimeException ex) { + throw new IOException(ex); + } + } + + private static void collectKeys(PGPPublicKeyRingCollection pgpPublicKeyRingCollection, + Consumer<PGPPublicKey> collector) { + pgpPublicKeyRingCollection.forEach(keyring -> collectKeys(keyring, collector)); + } + + private static void collectKeys(PGPPublicKeyRing pgpPublicKeyRing, Consumer<PGPPublicKey> collector) { + pgpPublicKeyRing.getPublicKeys().forEachRemaining(collector::accept); + } + + private static abstract class LocalKeyCache { + private Path cache; + private FileTime lastModifiedTime; + private List<PGPPublicKey> keys; + + public LocalKeyCache(Path cache) { + this.cache = cache; + } + + protected abstract void log(Throwable throwable); + + protected abstract List<PGPPublicKey> reconcileKeys(List<PGPPublicKey> keysToReconcile); + + public List<PGPPublicKey> get() { + if (keys != null) { + try { + FileTime newLastModifiedTime = Files.getLastModifiedTime(cache); + if (lastModifiedTime == null || lastModifiedTime.compareTo(newLastModifiedTime) < 0) { + lastModifiedTime = newLastModifiedTime; + } else { + return keys; + } + } catch (Exception e) { + //$FALL-THROUGH$ + } + } + + if (!Files.isRegularFile(cache)) { + return List.of(); + } + + try (InputStream input = new ArmoredInputStream(new BufferedInputStream(Files.newInputStream(cache)))) { + keys = loadKeys(input); + return keys; + } catch (IOException ex) { + log(ex); + try { + // Assume the cache is corrupt so delete it. + Files.delete(cache); + } catch (IOException ex2) { + log(ex2); + } + return List.of(); + } + } + + public void add(PGPPublicKey key) { + List<PGPPublicKey> oldKeys = get(); + List<PGPPublicKey> newKeys = new ArrayList<>(oldKeys); + newKeys.add(key); + newKeys = reconcileKeys(newKeys); + if (!oldKeys.equals(newKeys)) { + try (OutputStream underlyingStream = newAtomicOutputStream(cache); + OutputStream output = new ArmoredOutputStream(underlyingStream)) { + for (PGPPublicKey newKey : newKeys) { + newKey.encode(output); + } + } catch (IOException e) { + log(e); + return; + } + keys = newKeys; + } + } + } + + private static abstract class PGPKeyServer { + private final Map<Long, List<PGPPublicKey>> keyIDMap = new LinkedHashMap<>(); + + private final String keyServer; + + private final Path keyCache; + + public PGPKeyServer(String keyServer, Path baseCache) { + this.keyServer = keyServer; + keyCache = baseCache.resolve(keyServer.replace(':', '_')); + if (!Files.isDirectory(this.keyCache)) { + try { + Files.createDirectory(keyCache); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + protected abstract boolean isStale(Path path); + + protected abstract IStatus download(URI uri, OutputStream receiver, IProgressMonitor monitor); + + protected abstract void log(Throwable throwable); + + public List<PGPPublicKey> getKeys(long keyID) { + List<PGPPublicKey> keys = keyIDMap.get(keyID); + String hexKeyID = toHex(keyID); + Path cache = keyCache.resolve(hexKeyID + ".asc"); //$NON-NLS-1$ + boolean needsRemoteFetch = !Files.isRegularFile(cache) || isStale(cache); + if (keys == null || needsRemoteFetch) { + try { + Iterable<PGPPublicKey> fetchedKeys; + if (needsRemoteFetch) { + String link = "https://" + keyServer + "/pks/lookup?op=get&search=0x" + hexKeyID; //$NON-NLS-1$ //$NON-NLS-2$ + if (DEBUG_KEY_SERVICE) { + DebugHelper.debug("KeyServer", "Searching", "uri", link); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + URI uri = new URI(link); + fetchedKeys = fetchKeys(uri, cache); + } else { + try (InputStream input = new ArmoredInputStream( + new BufferedInputStream(Files.newInputStream(cache)))) { + fetchedKeys = loadKeys(input); + } + } + + List<PGPPublicKey> newKeys = new ArrayList<>(); + for (PGPPublicKey fetchedKey : fetchedKeys) { + long fetchedKeyID = fetchedKey.getKeyID(); + if (fetchedKeyID == keyID) { + newKeys.add(fetchedKey); + } + } + + keyIDMap.put(keyID, newKeys); + keys = newKeys; + } catch (URISyntaxException | IOException e) { + log(e); + if (keys == null || keys.isEmpty()) { + List<PGPPublicKey> newKeys = List.of(); + keyIDMap.put(keyID, newKeys); + keys = newKeys; + } + } + } + + return Collections.unmodifiableList(keys); + } + + protected Collection<PGPPublicKey> fetchKeys(URI uri, Path cache) throws IOException { + try { + ByteArrayOutputStream reciever = new ByteArrayOutputStream(); + IStatus download = download(uri, reciever, new NullProgressMonitor()); + if (!download.isOK()) { + // If the file is not found, save an empty file to prevent repeated attempts to + // download from this URI. + Throwable exception = download.getException(); + if (exception instanceof FileNotFoundException) { + log(exception); + } else { + if (exception != null) { + throw new IOException(download.getMessage(), exception); + } + throw new IOException(download.getMessage()); + } + } + List<PGPPublicKey> result; + byte[] bytes = reciever.toByteArray(); + try { + try (InputStream input = new ArmoredInputStream(new ByteArrayInputStream(bytes))) { + result = loadKeys(input); + } + } catch (IOException ex) { + log(ex); + // If the bytes can't be processed cache an empty file to prevent repeated + // attempts. + bytes = new byte[0]; + result = List.of(); + } + + try (OutputStream out = newAtomicOutputStream(cache)) { + out.write(bytes); + } + + return result; + } catch (IOException ex) { + // If the key server fails, load the cache if it exists. + if (Files.isRegularFile(cache)) { + try (InputStream input = new ArmoredInputStream( + new BufferedInputStream(Files.newInputStream(cache)))) { + return loadKeys(input); + } catch (IOException ex1) { + try { + // Assume the cache is corrupt so delete it. + Files.delete(cache); + } catch (IOException ex2) { + log(ex2); + } + // Rethrow original network failure exception with additional details + throw new IOException("Error while processing " + uri + " as well while processing the cache " //$NON-NLS-1$ //$NON-NLS-2$ + + cache + ": " + ex1.getMessage(), ex); //$NON-NLS-1$ + } + } + throw new IOException("Error while processing " + uri, ex); //$NON-NLS-1$ + } + } + + } + + private static List<PGPPublicKey> getGPGPubringKeys(long keyID) { + return GPGPubringCache.getKeys(keyID); + } + + private static class GPGPubringCache { + private static final Supplier<PGPPublicKeyRingCollection> GPG_PUBRING = getGPGPubring(); + private static volatile PGPPublicKeyRingCollection cachePubring; + private static volatile Map<Long, List<PGPPublicKey>> cache; + + public static List<PGPPublicKey> getKeys(long keyID) { + PGPPublicKeyRingCollection pubring = GPG_PUBRING.get(); + if (pubring != cachePubring) { + Map<Long, List<PGPPublicKey>> newCache = new LinkedHashMap<>(); + for (Iterator<PGPPublicKeyRing> keyRings = pubring.getKeyRings(); keyRings.hasNext();) { + for (PGPPublicKey key : keyRings.next()) { + long keyID2 = key.getKeyID(); + List<PGPPublicKey> keys = newCache.computeIfAbsent(keyID2, it -> new ArrayList<>()); + keys.add(key); + } + } + cache = newCache; + cachePubring = pubring; + } + + List<PGPPublicKey> result = cache.get(keyID); + return result == null ? List.of() : result; + } + } + + private static abstract class GPGPubringSupplier implements Supplier<PGPPublicKeyRingCollection> { + + private final Path pubring; + + private PGPPublicKeyRingCollection keyRingCollection; + + private FileTime lastModifiedTime; + + public GPGPubringSupplier(Path pubring) { + this.pubring = pubring; + try { + keyRingCollection = new PGPPublicKeyRingCollection(Collections.emptyList()); + } catch (IOException | PGPException e) { + // Cannot happen for an empty collection. + throw new RuntimeException(e); + } + } + + @Override + public PGPPublicKeyRingCollection get() { + try { + FileTime newLastModifiedTime = Files.getLastModifiedTime(pubring); + if (lastModifiedTime == null || lastModifiedTime.compareTo(newLastModifiedTime) < 0) { + lastModifiedTime = newLastModifiedTime; + keyRingCollection = buildPubring(); + } + } catch (Exception e) { + //$FALL-THROUGH$ + } + return keyRingCollection; + } + + protected abstract PGPPublicKeyRingCollection buildPubring() throws Exception; + } + + private static Supplier<PGPPublicKeyRingCollection> getGPGPubring() { + Path gpgDirectory = getGPPDirectory(); + Path pubringGpg = gpgDirectory.resolve("pubring.gpg"); //$NON-NLS-1$ + Path pubringKbx = gpgDirectory.resolve("pubring.kbx"); //$NON-NLS-1$ + + if (Files.isRegularFile(pubringGpg)) { + return new GPGPubringSupplier(pubringGpg) { + @Override + protected PGPPublicKeyRingCollection buildPubring() throws Exception { + try (InputStream input = new BufferedInputStream(Files.newInputStream(pubringGpg))) { + PGPPublicKeyRingCollection keyRingCollection = new PGPPublicKeyRingCollection(input, + new JcaKeyFingerprintCalculator()); + return keyRingCollection; + } + } + }; + } else if (Files.isRegularFile(pubringKbx)) { + return new GPGPubringSupplier(pubringKbx) { + @Override + protected PGPPublicKeyRingCollection buildPubring() throws Exception { + try (InputStream input = new BufferedInputStream(Files.newInputStream(pubringKbx))) { + KeyBox keyBox = new JcaKeyBoxBuilder().build(input); + List<PGPPublicKeyRing> pgpPublicKeyRings = new ArrayList<>(); + for (KeyBlob keyBlob : keyBox.getKeyBlobs()) { + switch (keyBlob.getType()) { + case OPEN_PGP_BLOB: { + PGPPublicKeyRing pgpPublicKeyRing = ((PublicKeyRingBlob) keyBlob).getPGPPublicKeyRing(); + pgpPublicKeyRings.add(pgpPublicKeyRing); + } + default: { + //$FALL-THROUGH$ + } + } + } + PGPPublicKeyRingCollection keyRingCollection = new PGPPublicKeyRingCollection( + pgpPublicKeyRings); + return keyRingCollection; + } + } + }; + } else { + PGPPublicKeyRingCollection empty; + try { + empty = new PGPPublicKeyRingCollection(Collections.emptyList()); + } catch (IOException | PGPException e) { + // Cannot happen for an empty collection. + throw new RuntimeException(e); + } + return () -> empty; + } + } + + @SuppressWarnings("nls") + private static Path getGPPDirectory() { + // Handle ~ as might be used on macos and linux. + Function<String, Path> resolveTilde = s -> { + if (s.startsWith("~/") || s.startsWith("~" + File.separatorChar)) { + return new File(System.getProperty("user.home"), s.substring(2)).getAbsoluteFile().toPath(); + } + return Paths.get(s); + }; + + // Allow the user to specify the GPG home used by p2 specifically. + Path path = checkDirectory(System.getProperty(GPG_HOME_PROPERTY), resolveTilde); + if (path != null) { + return path; + } + + path = checkDirectory(System.getenv("GNUPGHOME"), resolveTilde); + if (path != null) { + return path; + } + + if ("win32".equals(System.getProperty("osgi.os"))) { + // On Windows prefer %APPDATA%\gnupg if it exists, even if Cygwin is used. + path = checkDirectory(System.getenv("APPDATA"), //$NON-NLS-1$ + s -> Paths.get(s).resolve("gnupg")); //$NON-NLS-1$ + if (path != null) { + return path; + } + } + // All systems, including Cygwin and even Windows if %APPDATA%\gnupg doesn't + // exist. + return resolveTilde.apply("~/.gnupg"); //$NON-NLS-1$ + } + + private static Path checkDirectory(String dir, Function<String, Path> toPath) { + if (dir != null && !dir.isBlank()) { + try { + Path directory = toPath.apply(dir); + if (Files.isDirectory(directory)) { + return directory; + } + } catch (RuntimeException e) { + //$FALL-THROUGH$ + } + } + return null; + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/IArtifactUIServices.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/IArtifactUIServices.java new file mode 100644 index 000000000..b5cc328f5 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/IArtifactUIServices.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.p2.repository.artifact.spi; + +import java.io.File; +import java.security.cert.Certificate; +import java.util.*; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.eclipse.equinox.p2.core.UIServices; +import org.eclipse.equinox.p2.core.UIServices.TrustInfo; +import org.eclipse.equinox.p2.metadata.IArtifactKey; + +/** + * An interface optionally implemented by {@link UIServices} to provide richer + * information for the users. In particular, the users often wish to know which + * artifacts are signed by which certificates or which keys when they are + * {@link UIServices#getTrustInfo(Certificate[][], Collection, String[]) + * prompted} whether to trust such certificates or keys. + * + * @since 2.6 + * + * @see UIServices#getTrustInfo(Certificate[][], Collection, String[]) + */ +public interface IArtifactUIServices { + + /** + * Opens a UI prompt to capture information about trusted content. + * + * @param untrustedCertificateChains a map from untrusted certificate chains to + * the set of keys of the artifacts signed by + * that chain. + * @param untrustedPGPKeys a map of untrusted PGP public keys to the + * set of keys of the artifacts signed by that + * key. + * @param unsignedArtifacts a set of keys of the artifacts that are not + * signed. + * @param artifactFiles a map from artifact keys to the file + * associated with that artifact key. + * + * @return the TrustInfo that describes the user's choices for trusting + * certificates, keys, and unsigned content. + * + * @see #getTrustInfo(UIServices, Map, Map, Set, Map) + * @see UIServices + */ + TrustInfo getTrustInfo( // + Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificateChains, // + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifactFiles); + + /** + * Opens a UI prompt to capture information about trusted content. This + * implementation is useful for delegating to an old-style + * {@link UIServices#getTrustInfo(Certificate[][], Collection, String[]) + * UIServices implementation} that does not support the IArtifactUIServices + * interface. + * + * @param uiServices the delegate UI services. + * @param untrustedCertificateChains a map from untrusted certificate chains to + * the set of keys of the artifacts signed by + * that chain. + * @param untrustedPGPKeys a map of untrusted PGP public keys to the + * set of keys of the artifacts signed by that + * key. + * @param unsignedArtifacts a set of keys of the artifacts that are not + * signed. + * @param artifactFiles a map from artifact keys to the file + * associated with that artifact key. + * + * @return the TrustInfo that describes the user's choices for trusting + * certificates, keys, and unsigned content. + * + * @see #getTrustInfo(Map, Map, Set, Map) + * @see UIServices#getTrustInfo(Certificate[][], Collection, String[]) + */ + static TrustInfo getTrustInfo(UIServices uiServices, // + Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificateChains, // + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifactFiles) { + Certificate[][] unTrustedCertificateChainsArray = untrustedCertificateChains.keySet().stream() + .map(c -> c.toArray(Certificate[]::new)).toArray(Certificate[][]::new); + String[] details = unsignedArtifacts.isEmpty() ? null + : unsignedArtifacts.stream().map(artifactFiles::get).map(Objects::toString).toArray(String[]::new); + return uiServices.getTrustInfo(unTrustedCertificateChainsArray, untrustedPGPKeys.keySet(), details); + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/PGPPublicKeyService.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/PGPPublicKeyService.java new file mode 100644 index 000000000..6fbdff286 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/PGPPublicKeyService.java @@ -0,0 +1,243 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.p2.repository.spi; + +import java.util.*; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.util.encoders.Hex; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory; + +/** + * A service for managing and searching {@link PGPPublicKey keys}. + * Implementations may make use of a + * <a href="https://datatracker.ietf.org/doc/html/draft-shaw-openpgp-hkp-00">key + * server</a> to fetch up-to-date information about keys. Implementations should + * generally provide support for caching and efficient lookup of keys, + * especially lookup based on {@link PGPPublicKey#getKeyID() key ID} because + * signatures generally use {@link PGPSignature#getKeyID() key IDs} and this is + * the primary use case. + * + * <p> + * Implementors of this service are responsible for registering the + * implementation with the {@link IProvisioningAgent provisioning agent} either + * {@link IProvisioningAgent#registerService(String, Object) explicitly} or via + * an {@link IAgentServiceFactory agent service factory}. + * </p> + * + * @see PGPPublicKey#getKeyID() + * @see PGPSignature#getKeyID() + * @see IAgentServiceFactory + * @see IProvisioningAgent#registerService(String, Object) + * + * @since 2.6 + */ +public abstract class PGPPublicKeyService { + /** + * The name used for obtaining a reference to the key service. + * + * @see IProvisioningAgent#getService(Class) + * @see IProvisioningAgent#getService(String) + */ + public static final String SERVICE_NAME = PGPPublicKeyService.class.getName(); + + /** + * Returns the key associated with the given + * {@link PGPPublicKey#getFingerprint() fingerprint}. + * + * @param fingerprint the fingerprint for which to search. + * @return the key with the matching fingerprint. + * + * @see PGPPublicKey#getFingerprint() + */ + public PGPPublicKey getKey(byte[] fingerprint) { + return getKey(toHex(fingerprint)); + } + + /** + * Returns the key associated with the given + * {@link PGPPublicKey#getFingerprint() fingerprint} represented as a + * {@link #toHex(byte[]) hexadecimal} string value. + * + * @param fingerprint the fingerprint for which to search. + * @return the key with the matching fingerprint. + * + * @see PGPPublicKey#getFingerprint() + * @see #toHex(byte[]) + */ + public abstract PGPPublicKey getKey(String fingerprint); + + /** + * Returns the keys associated with the given {@link PGPPublicKey#getKeyID() key + * ID}. In general, key ID collisions are possible so implementations must be + * tolerant of that. + * + * @param keyID the key ID for which to search. + * @return the keys with the matching key IDs. + * + * @see PGPPublicKey#getKeyID() + * @see PGPSignature#getKeyID() + */ + public abstract Collection<PGPPublicKey> getKeys(long keyID); + + /** + * Adds the given key to this key service. An implementations may fetch more + * up-to-date information about this key from a key server and may return a + * different key than the one passed in here. In general an implementation may + * also return an existing key, with the same fingerprint, already known to the + * key service. + * + * @param key the key to add. + * @return the normalized key available in this key service. + */ + public abstract PGPPublicKey addKey(PGPPublicKey key); + + /** + * Returns the set of keys that have been verified to have signed the given key. + * These are the links in the web of trust. + * + * @param key the key for which to find keys that have signed it. + * @return the set of keys that have been verified to have signed the given key. + * + * @see PGPSignature#verifyCertification(String, PGPPublicKey) + * @see PGPSignature#verifyCertification(PGPPublicKey, PGPPublicKey) + */ + public abstract Set<PGPPublicKey> getVerifiedCertifications(PGPPublicKey key); + + /** + * If this key has a revocation signature that is verified to have been signed + * by the public key of that revocation signature, this returns the + * {@link PGPSignature#getCreationTime() creation time} of that signature, + * otherwise it returns <code>null</code>. + * + * @param key the key to test for revocation. + * @return when this key was verifiably revoked, or <code>null</code> if it is + * not revoked. + * + * @see PGPSignature#getKeyID() + * @see PGPPublicKey#hasRevocation() + * @see PGPSignature#getCreationTime() + * @see PGPSignature#KEY_REVOCATION + * @see PGPSignature#SUBKEY_REVOCATION + */ + public abstract Date getVerifiedRevocationDate(PGPPublicKey key); + + /** + * Returns whether the signature's {@link PGPSignature creation time} is before + * the key's {@link #getVerifiedRevocationDate(PGPPublicKey) verified revocation + * date}, if that key has one. + * + * @param signature the signature to test. + * @param key the corresponding key of this signature against which to + * test. + * @return <code>true</code> if the signature was created before the key was + * revoked or if the key is not revoked, <code>false</code> otherwise. + * + * @throws IllegalArgumentException if the signature's + * {@link PGPSignature#getKeyID() key} is not + * the same as the key's + * {@link PGPPublicKey#getKeyID() key ID} + */ + public boolean isCreatedBeforeRevocation(PGPSignature signature, PGPPublicKey key) { + if (signature.getKeyID() != key.getKeyID()) { + throw new IllegalArgumentException("The signature's key ID must be the same as the key's key ID"); //$NON-NLS-1$ + } + Date verifiedRevocationDate = getVerifiedRevocationDate(key); + if (verifiedRevocationDate != null) { + long signatureCreationTime = signature.getCreationTime().getTime(); + if (signatureCreationTime >= verifiedRevocationDate.getTime()) { + return false; + } + } + return true; + } + + /** + * Returns the hexadecimal representation of the key's + * {@link PGPPublicKey#getFingerprint() fingerprint}. + * + * @param key the key for which to get the hexadecimal fingerprint. + * + * @return the hexadecimal representation of the key's fingerprint. + */ + public static String toHexFingerprint(PGPPublicKey key) { + return Hex.toHexString(key.getFingerprint()); + } + + /** + * Returns the hexadecimal representation of the given bytes. + * + * @param bytes the bytes to convert to a hexadecimal representation. + * @return the hexadecimal representation of the given bytes. + */ + public static String toHex(byte[] bytes) { + return Hex.toHexString(bytes); + } + + /** + * Returns the hexadecimal representation of the given long value, typically a + * key ID, padded with leading zeros to a length of 16. + * + * @param keyID the long value, typically a key ID, to convert to a hexadecimal + * representation. + * @return the hexadecimal representation of the given long value, padded with + * leading zeros. + */ + public static String toHex(long keyID) { + return String.format("%1$016x", keyID); //$NON-NLS-1$ + } + + /** + * If the signature's {@link PGPSignature#getCreationTime() creation time} is + * before the key's {@link PGPPublicKey#getCreationTime() creation time}, this + * returns a negative value equal to the number of milliseconds that the + * signature was created before the key was created. If the key has a + * {@link PGPPublicKey#getValidSeconds() validity period}, i.e., if the key + * expires, and the signature's creation time is after the key's expiration, + * returns a positive value equal to the number of milliseconds that the + * signature was created after the key's expiration. Otherwise, the signature + * was created during the period of time that the key was valid and this returns + * <code>0</code>. + * + * @param signature the signature to test. + * @param key the corresponding key of this signature against which to + * test. + * @return a negative value representing the number of milliseconds the + * signature was created before the key was created, a positive value + * representing the number of milliseconds the signature as created + * after the key expired, or <code>0</code> if the signature was created + * during the valid period of the key. + * @throws IllegalArgumentException if the signature's + * {@link PGPSignature#getKeyID() key} is not + * the same as the key's + * {@link PGPPublicKey#getKeyID() key ID} + */ + public static long compareSignatureTimeToKeyValidityTime(PGPSignature signature, PGPPublicKey key) { + if (signature.getKeyID() != key.getKeyID()) { + throw new IllegalArgumentException("The signature's key ID must be the same as the key's key ID"); //$NON-NLS-1$ + } + long keyCreationTime = key.getCreationTime().getTime(); + long signatureCreationTime = signature.getCreationTime().getTime(); + long delta = signatureCreationTime - keyCreationTime; + if (delta < 0) { + return delta; + } + long validSeconds = key.getValidSeconds(); + if (validSeconds != 0) { + delta = signatureCreationTime - (keyCreationTime + validSeconds * 1000); + if (delta > 0) { + return delta; + } + } + return 0; + } +} diff --git a/bundles/org.eclipse.equinox.p2.sar/pom.xml b/bundles/org.eclipse.equinox.p2.sar/pom.xml index 2a1c6de4c..fb9f99428 100644 --- a/bundles/org.eclipse.equinox.p2.sar/pom.xml +++ b/bundles/org.eclipse.equinox.p2.sar/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.tests.discovery/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.tests.discovery/META-INF/MANIFEST.MF index 6ccf36ca3..361f71e5e 100644 --- a/bundles/org.eclipse.equinox.p2.tests.discovery/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.tests.discovery/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.equinox.p2.tests.discovery;singleton:=true -Bundle-Version: 1.3.100.qualifier +Bundle-Version: 1.3.200.qualifier Bundle-Vendor: %Bundle-Vendor Bundle-Localization: plugin Export-Package: org.eclipse.equinox.p2.discovery.tests;x-internal:=true, diff --git a/bundles/org.eclipse.equinox.p2.tests.discovery/plugin.xml b/bundles/org.eclipse.equinox.p2.tests.discovery/plugin.xml index 968754bef..f6f6a95f0 100644 --- a/bundles/org.eclipse.equinox.p2.tests.discovery/plugin.xml +++ b/bundles/org.eclipse.equinox.p2.tests.discovery/plugin.xml @@ -86,7 +86,7 @@ style="push"> <parameter name="org.eclipse.equinox.p2.ui.discovery.commands.DirectoryParameter" - value="http://www.eclipse.org/mylyn/discovery/directory-3.3.xml"> + value="https://www.eclipse.org/mylyn/discovery/directory-3.3.xml"> </parameter> <parameter name="org.eclipse.equinox.p2.ui.discovery.commands.TagsParameter" @@ -99,7 +99,7 @@ style="push"> <parameter name="org.eclipse.equinox.p2.ui.discovery.commands.RepositoryParameter" - value="http://download.eclipse.org/tools/mylyn/update/e3.4"> + value="https://download.eclipse.org/tools/mylyn/update/e3.4"> </parameter> </command> </menuContribution> diff --git a/bundles/org.eclipse.equinox.p2.tests.discovery/pom.xml b/bundles/org.eclipse.equinox.p2.tests.discovery/pom.xml index 1a7e81a20..ce3ff89b1 100644 --- a/bundles/org.eclipse.equinox.p2.tests.discovery/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.discovery/pom.xml @@ -4,12 +4,12 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.tests.discovery</artifactId> - <version>1.3.100-SNAPSHOT</version> + <version>1.3.200-SNAPSHOT</version> <packaging>eclipse-test-plugin</packaging> <properties> diff --git a/bundles/org.eclipse.equinox.p2.tests.optimizers/pom.xml b/bundles/org.eclipse.equinox.p2.tests.optimizers/pom.xml index 3e89aab0c..089a4d389 100644 --- a/bundles/org.eclipse.equinox.p2.tests.optimizers/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.optimizers/pom.xml @@ -4,7 +4,7 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.tests.reconciler.product/pom.xml b/bundles/org.eclipse.equinox.p2.tests.reconciler.product/pom.xml index 95344861e..13eefc82a 100644 --- a/bundles/org.eclipse.equinox.p2.tests.reconciler.product/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.reconciler.product/pom.xml @@ -11,7 +11,7 @@ <parent> <groupId>org.eclipse</groupId> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> diff --git a/bundles/org.eclipse.equinox.p2.tests.reconciler.product/reconciler.product b/bundles/org.eclipse.equinox.p2.tests.reconciler.product/reconciler.product index 6cd3000e1..0532b3ab5 100644 --- a/bundles/org.eclipse.equinox.p2.tests.reconciler.product/reconciler.product +++ b/bundles/org.eclipse.equinox.p2.tests.reconciler.product/reconciler.product @@ -38,8 +38,8 @@ <plugin id="org.apache.commons.jxpath"/> <plugin id="org.apache.commons.logging"/> <plugin id="org.apache.felix.scr"/> - <plugin id="org.apache.httpcomponents.httpclient"/> - <plugin id="org.apache.httpcomponents.httpcore"/> + <plugin id="org.apache.httpcomponents.client5.httpclient5"/> + <plugin id="org.apache.httpcomponents.core5.httpcore5"/> <plugin id="org.apache.xmlgraphics"/> <plugin id="org.eclipse.core.commands"/> <plugin id="org.eclipse.core.contenttype"/> @@ -49,7 +49,7 @@ <plugin id="org.eclipse.core.expressions"/> <plugin id="org.eclipse.core.jobs"/> <plugin id="org.eclipse.core.net"/> - <plugin id="org.eclipse.core.net.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.core.net.linux" fragment="true"/> <plugin id="org.eclipse.core.runtime"/> <plugin id="org.eclipse.e4.core.commands"/> <plugin id="org.eclipse.e4.core.contexts"/> @@ -78,7 +78,7 @@ <plugin id="org.eclipse.ecf.filetransfer"/> <plugin id="org.eclipse.ecf.identity"/> <plugin id="org.eclipse.ecf.provider.filetransfer"/> - <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient45"/> + <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient5"/> <plugin id="org.eclipse.ecf.provider.filetransfer.source"/> <plugin id="org.eclipse.ecf.provider.filetransfer.ssl" fragment="true"/> <plugin id="org.eclipse.ecf.ssl" fragment="true"/> @@ -120,7 +120,7 @@ <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.security.ui"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> <plugin id="org.eclipse.equinox.simpleconfigurator.manipulator"/> diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF index 37792cc02..58c4adbf1 100644 --- a/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.tests.ui/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.tests.ui -Bundle-Version: 1.3.300.qualifier +Bundle-Version: 1.3.400.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Require-Bundle: org.eclipse.core.runtime;bundle-version="3.4.100", diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml b/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml index 2f8b7876a..f941878fb 100644 --- a/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.ui/pom.xml @@ -15,13 +15,13 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.tests.ui</artifactId> - <version>1.3.300-SNAPSHOT</version> + <version>1.3.400-SNAPSHOT</version> <packaging>eclipse-test-plugin</packaging> <properties> diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/actions/ElementUtilsTest.java b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/actions/ElementUtilsTest.java index d0125ad1e..a4cfbd144 100644 --- a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/actions/ElementUtilsTest.java +++ b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/actions/ElementUtilsTest.java @@ -58,15 +58,15 @@ public class ElementUtilsTest extends ProfileModificationActionTest { public void testUpdateUsingElements() throws URISyntaxException { // Two visible repos, one is added, the other is not - URI known1 = new URI("http://example.com/known1"); - URI known2 = new URI("http://example.com/known2"); + URI known1 = new URI("https://example.com/known1"); + URI known2 = new URI("https://example.com/known2"); IMetadataRepositoryManager manager = getMetadataRepositoryManager(); manager.addRepository(known1); // Add system repos that should not be known or affected by ElementUtils // One is an enabled system repo, one is disabled system repo - URI uri = new URI("http://example.com/1"); - URI uri2 = new URI("http://example.com/2"); + URI uri = new URI("https://example.com/1"); + URI uri2 = new URI("https://example.com/2"); manager.addRepository(uri); manager.setRepositoryProperty(uri, IRepository.PROP_SYSTEM, Boolean.toString(true)); manager.addRepository(uri2); @@ -82,7 +82,7 @@ public class ElementUtilsTest extends ProfileModificationActionTest { MetadataRepositoryElement[] elements = children.toArray(new MetadataRepositoryElement[children.size()]); // Add a visible repo not known by the elements - URI uri3 = new URI("http://example.com/3"); + URI uri3 = new URI("https://example.com/3"); manager.addRepository(uri3); // Now update the repo using the elements. diff --git a/bundles/org.eclipse.equinox.p2.tests.verifier/pom.xml b/bundles/org.eclipse.equinox.p2.tests.verifier/pom.xml index 909390b0d..08bb6782c 100644 --- a/bundles/org.eclipse.equinox.p2.tests.verifier/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests.verifier/pom.xml @@ -4,7 +4,7 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF index 7859591e4..73c3d22e7 100644 --- a/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.tests;singleton:=true -Bundle-Version: 1.8.600.qualifier +Bundle-Version: 1.8.700.qualifier Bundle-ClassPath: . Bundle-Activator: org.eclipse.equinox.p2.tests.TestActivator Bundle-Vendor: %providerName @@ -45,7 +45,7 @@ Require-Bundle: org.eclipse.equinox.frameworkadmin, org.eclipse.ecf.filetransfer;bundle-version="4.0.0", org.eclipse.ecf.identity;bundle-version="3.1.0", org.eclipse.ecf.provider.filetransfer;bundle-version="3.1.0", - org.eclipse.ecf.provider.filetransfer.httpclient45, + org.eclipse.ecf.provider.filetransfer.httpclient5, org.eclipse.equinox.p2.reconciler.dropins;bundle-version="1.1.0", org.eclipse.ant.core;bundle-version="3.2.200", org.apache.ant;bundle-version="1.7.1", @@ -114,6 +114,5 @@ Import-Package: org.eclipse.ant.core, org.eclipse.osgi.util;version="1.1.0", org.osgi.framework;version="1.3.0", org.osgi.service.packageadmin;version="1.2.0", - org.osgi.util.tracker;version="1.3.0", - org.easymock;version="4.0.0" + org.osgi.util.tracker;version="1.3.0" Automatic-Module-Name: org.eclipse.equinox.p2.tests diff --git a/bundles/org.eclipse.equinox.p2.tests/plugin.xml b/bundles/org.eclipse.equinox.p2.tests/plugin.xml index 841b529ea..857699598 100644 --- a/bundles/org.eclipse.equinox.p2.tests/plugin.xml +++ b/bundles/org.eclipse.equinox.p2.tests/plugin.xml @@ -153,4 +153,10 @@ suffix="@testArtifactRepositoryRegistry"> </filter> </extension> + <extension + point="org.eclipse.equinox.p2.engine.pgp"> + <trustedKeys + path="testData/pgp/signer2-publickey.asc"> + </trustedKeys> + </extension> </plugin> diff --git a/bundles/org.eclipse.equinox.p2.tests/pom.xml b/bundles/org.eclipse.equinox.p2.tests/pom.xml index f0006e807..894085114 100644 --- a/bundles/org.eclipse.equinox.p2.tests/pom.xml +++ b/bundles/org.eclipse.equinox.p2.tests/pom.xml @@ -10,13 +10,13 @@ <parent> <groupId>org.eclipse</groupId> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.tests</artifactId> - <version>1.8.600-SNAPSHOT</version> + <version>1.8.700-SNAPSHOT</version> <packaging>eclipse-test-plugin</packaging> <properties> diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java index dcc84bbfb..ab74b9572 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java @@ -739,7 +739,7 @@ public abstract class AbstractProvisioningTest extends TestCase { } } - public static void writeBuffer(File outputFile, StringBuffer buffer) throws IOException { + public static void writeBuffer(File outputFile, StringBuilder buffer) throws IOException { outputFile.getParentFile().mkdirs(); try (FileOutputStream stream = new FileOutputStream(outputFile)) { stream.write(buffer.toString().getBytes()); @@ -1367,7 +1367,7 @@ public abstract class AbstractProvisioningTest extends TestCase { IInstallableUnit sourceIU = it.next(); IQueryResult destinationCollector = destination.query(QueryUtil.createIUQuery(sourceIU), null); assertEquals(message, 1, queryResultSize(destinationCollector)); - assertTrue(message, sourceIU.equals(destinationCollector.iterator().next())); + assertEquals(message, sourceIU, destinationCollector.iterator().next()); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestData.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestData.java index c423c5dd5..d2a8bc870 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestData.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestData.java @@ -135,15 +135,16 @@ public class TestData { * @throws IOException */ public static void assertEquals(ZipInputStream expected, ZipInputStream actual) throws IOException { - Map<String, Object[]> jar1 = getEntries(expected); - Map<String, Object[]> jar2 = getEntries(actual); - for (String name : jar1.keySet()) { - Object[] file1 = jar1.get(name); - Object[] file2 = jar2.remove(name); - Assert.assertNotNull(file2); - - ZipEntry entry1 = (ZipEntry) file1[0]; - ZipEntry entry2 = (ZipEntry) file2[0]; + Map<String, Object[]> expectedEntries = getEntries(expected); + Map<String, Object[]> actualEntries = getEntries(actual); + for (String name : expectedEntries.keySet()) { + Object[] expectedFiles = expectedEntries.get(name); + Object[] actualFiles = actualEntries.remove(name); + Assert.assertNotNull(name + " entry is missing in actual zip stream (actual=" + actualEntries.keySet() + + ", expected=" + expectedEntries.keySet() + ")", actualFiles); + + ZipEntry entry1 = (ZipEntry) expectedFiles[0]; + ZipEntry entry2 = (ZipEntry) actualFiles[0]; // compare the entries Assert.assertEquals(entry1.getName(), entry2.getName()); Assert.assertEquals(entry1.getSize(), entry2.getSize()); @@ -154,11 +155,11 @@ public class TestData { Assert.assertEquals(entry1.getMethod(), entry2.getMethod()); // check the content of the entries - Assert.assertArrayEquals((byte[]) file1[1], (byte[]) file2[1]); + Assert.assertArrayEquals((byte[]) expectedFiles[1], (byte[]) actualFiles[1]); } // ensure that we have consumed all of the entries in the second JAR - Assert.assertEquals(0, jar2.size()); + Assert.assertEquals(0, actualEntries.size()); } /** diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/PGPSignatureVerifierTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/PGPSignatureVerifierTest.java index 36b50a545..dd3356767 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/PGPSignatureVerifierTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/PGPSignatureVerifierTest.java @@ -13,31 +13,57 @@ package org.eclipse.equinox.p2.tests.artifact.processors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.nio.file.Files; +import java.util.Set; import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IStatus; -import org.eclipse.equinox.internal.p2.artifact.processors.pgp.Messages; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; +import org.eclipse.equinox.internal.provisional.p2.repository.DefaultPGPPublicKeyService; +import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; import org.eclipse.equinox.p2.repository.artifact.IProcessingStepDescriptor; import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; import org.eclipse.equinox.p2.repository.artifact.spi.ProcessingStepDescriptor; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.equinox.p2.tests.TestAgentProvider; import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; public class PGPSignatureVerifierTest { + @Rule + public TestAgentProvider agentProvider = new TestAgentProvider(); + + @Before + public void initialize() { + try { + PGPPublicKeyService keyService = agentProvider.getService(PGPPublicKeyService.class); + if (keyService instanceof DefaultPGPPublicKeyService) { + DefaultPGPPublicKeyService defaultPGPPublicKeyService = (DefaultPGPPublicKeyService) keyService; + defaultPGPPublicKeyService.setKeyServers(Set.of()); + defaultPGPPublicKeyService.setGPG(false); + } + } catch (ProvisionException e) { + //$FALL-THROUGH$ + } + } + // @formatter:off /* - * About test keys: - * * Install the public&private keys locally - * * then generate signatures with eg `gpg -u signer2@fakeuser.eclipse.org -a --output signed_by_signer_2 --detach-sig testArtifact` + * About test keys: * Install the public&private keys locally * then generate + * signatures with eg `gpg -u signer2@fakeuser.eclipse.org -a --output + * signed_by_signer_2 --detach-sig testArtifact` */ // @formatter:on @@ -50,6 +76,23 @@ public class PGPSignatureVerifierTest { return res; } + private static class ArtifactOutputStream extends ByteArrayOutputStream implements IAdaptable { + IArtifactDescriptor descriptor = new ArtifactDescriptor( + new ArtifactKey("whatever", "whatever", Version.parseVersion("1.0.0"))); + + public IArtifactDescriptor getDescriptor() { + return descriptor; + } + + @Override + public <T> T getAdapter(Class<T> adapter) { + if (adapter.isInstance(descriptor)) { + return adapter.cast(descriptor); + } + return null; + } + } + private String read(String resource) throws IOException, URISyntaxException { return Files.readString(new File(FileLocator.toFileURL(getClass().getResource(resource)).toURI()).toPath()); } @@ -58,8 +101,11 @@ public class PGPSignatureVerifierTest { public void testOK() throws Exception { IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); IArtifactDescriptor artifact = createArtifact("signed_by_signer_1", "public_signer1.pgp"); + @SuppressWarnings("resource") PGPSignatureVerifier verifier = new PGPSignatureVerifier(); - verifier.initialize(null, processingStepDescriptor, artifact); + verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); + ArtifactOutputStream artifactOutputStream = new ArtifactOutputStream(); + verifier.link(artifactOutputStream, new NullProgressMonitor()); Assert.assertTrue(verifier.getStatus().toString(), verifier.getStatus().isOK()); try (InputStream bytes = getClass().getResourceAsStream("testArtifact")) { bytes.transferTo(verifier); @@ -67,6 +113,12 @@ public class PGPSignatureVerifierTest { Assert.assertTrue(verifier.getStatus().isOK()); verifier.close(); Assert.assertTrue(verifier.getStatus().isOK()); + + IArtifactDescriptor descriptor = artifactOutputStream.getDescriptor(); + Assert.assertNotNull("Signatures should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNotNull("Keys should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } @Test @@ -74,10 +126,22 @@ public class PGPSignatureVerifierTest { IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); IArtifactDescriptor artifact = createArtifact("signed_by_signer_1", "public_signer2.pgp"); try (PGPSignatureVerifier verifier = new PGPSignatureVerifier()) { - verifier.initialize(null, processingStepDescriptor, artifact); - IStatus status = verifier.getStatus(); - assertEquals(IStatus.ERROR, status.getSeverity()); - assertTrue(status.getMessage().contains("Public key not found for")); + verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); + ArtifactOutputStream artifactOutputStream = new ArtifactOutputStream(); + verifier.link(artifactOutputStream, new NullProgressMonitor()); + Assert.assertTrue(verifier.getStatus().toString(), verifier.getStatus().isOK()); + try (InputStream bytes = getClass().getResourceAsStream("testArtifact")) { + bytes.transferTo(verifier); + } + Assert.assertTrue(verifier.getStatus().isOK()); + verifier.close(); + Assert.assertTrue(verifier.getStatus().isOK()); + + IArtifactDescriptor descriptor = artifactOutputStream.getDescriptor(); + Assert.assertNull("No signatures should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNull("No keys should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } } @@ -86,7 +150,7 @@ public class PGPSignatureVerifierTest { IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); IArtifactDescriptor artifact = createArtifact("signed_by_signer_1_tampered", "public_signer1.pgp"); try (PGPSignatureVerifier verifier = new PGPSignatureVerifier()) { - verifier.initialize(null, processingStepDescriptor, artifact); + verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); // signature has random modification, making it invalid by itself Assert.assertFalse(verifier.getStatus().isOK()); } @@ -96,8 +160,9 @@ public class PGPSignatureVerifierTest { public void testSignatureForAnotherArtifact() throws Exception { IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); IArtifactDescriptor artifact = createArtifact("signed_by_signer_1_otherArtifact", "public_signer1.pgp"); + @SuppressWarnings("resource") PGPSignatureVerifier verifier = new PGPSignatureVerifier(); - verifier.initialize(null, processingStepDescriptor, artifact); + verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); Assert.assertTrue(verifier.getStatus().isOK()); try (InputStream bytes = getClass().getResourceAsStream("testArtifact")) { bytes.transferTo(verifier); @@ -106,6 +171,6 @@ public class PGPSignatureVerifierTest { verifier.close(); IStatus status = verifier.getStatus(); assertEquals(IStatus.ERROR, status.getSeverity()); - assertTrue(status.getMessage().contains(Messages.Error_SignatureAndFileDontMatch)); + assertTrue(status.getMessage().matches(".*signature.*invalid.*")); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/PGPVerifierTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/PGPVerifierTest.java index c010af306..0d4525b23 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/PGPVerifierTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/PGPVerifierTest.java @@ -10,15 +10,26 @@ *******************************************************************************/ package org.eclipse.equinox.p2.tests.artifact.repository; +import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.Set; +import java.util.stream.Stream; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; import org.eclipse.equinox.internal.p2.artifact.repository.MirrorRequest; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; +import org.eclipse.equinox.internal.provisional.p2.repository.DefaultPGPPublicKeyService; +import org.eclipse.equinox.p2.core.IAgentLocation; import org.eclipse.equinox.p2.metadata.Version; +import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.equinox.p2.tests.AbstractProvisioningTest; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -30,7 +41,6 @@ public class PGPVerifierTest extends AbstractProvisioningTest { @Before public void setUp() throws Exception { super.setUp(); - PGPSignatureVerifier.KNOWN_KEYS.clear(); } private void loadPGPTestRepo(String repoName) throws Exception { @@ -42,29 +52,75 @@ public class PGPVerifierTest extends AbstractProvisioningTest { @Test public void testAllGood() throws Exception { - IStatus mirrorStatus = performMirrorFrom("repoPGPOK"); + MirrorRequest mirrorRequest = performMirrorFrom("repoPGPOK"); + IStatus mirrorStatus = mirrorRequest.getResult(); assertOK(mirrorStatus); + + IArtifactDescriptor[] artifactDescriptors = targetRepo.getArtifactDescriptors(mirrorRequest.getArtifactKey()); + Assert.assertEquals(1, artifactDescriptors.length); + IArtifactDescriptor descriptor = artifactDescriptors[0]; + Assert.assertNotNull("Signatures should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNotNull("Keys should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } @Test public void testAllGoodWithEncodedProperties() throws Exception { - IStatus mirrorStatus = performMirrorFrom("repoPGPOK_encoded"); + MirrorRequest mirrorRequest = performMirrorFrom("repoPGPOK_encoded"); + IStatus mirrorStatus = mirrorRequest.getResult(); assertOK(mirrorStatus); + + IArtifactDescriptor[] artifactDescriptors = targetRepo.getArtifactDescriptors(mirrorRequest.getArtifactKey()); + Assert.assertEquals(1, artifactDescriptors.length); + IArtifactDescriptor descriptor = artifactDescriptors[0]; + Assert.assertNotNull("Signatures should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNotNull("Keys should be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } - private IStatus performMirrorFrom(String repoName) throws Exception { + private MirrorRequest performMirrorFrom(String repoName) throws Exception { + // Clear the remembered keys/cache of the agent. + IAgentLocation agentLocation = getAgent().getService(IAgentLocation.class); + Path repositoryCache = Paths + .get(agentLocation.getDataArea(org.eclipse.equinox.internal.p2.repository.Activator.ID)); + if (Files.isDirectory(repositoryCache)) { + try (Stream<Path> walk = Files.walk(repositoryCache)) { + walk.sorted(Comparator.reverseOrder()).forEach(path -> { + try { + Files.delete(path); + } catch (IOException e) { + // Ignore + } + }); + } + } + DefaultPGPPublicKeyService keyService = new DefaultPGPPublicKeyService(getAgent()); + keyService.setGPG(false); + keyService.setKeyServers(Set.of()); + + getAgent().registerService(PGPPublicKeyService.SERVICE_NAME, keyService); loadPGPTestRepo(repoName); ArtifactKey key = new ArtifactKey("osgi.bundle", "blah", Version.create("1.0.0.123456")); MirrorRequest mirrorRequest = new MirrorRequest(key, targetRepo, NO_PROPERTIES, NO_PROPERTIES, getTransport()); mirrorRequest.perform(sourceRepo, getMonitor()); - return mirrorRequest.getResult(); + return mirrorRequest; } @Test public void testMissingPublicKey() throws Exception { - IStatus mirrorStatus = performMirrorFrom("repoMissingPublicKey"); - assertNotOK(mirrorStatus); - assertTrue(mirrorStatus.toString().contains("Public key not found")); + MirrorRequest mirrorRequest = performMirrorFrom("repoMissingPublicKey"); + IStatus mirrorStatus = mirrorRequest.getResult(); + assertOK(mirrorStatus); + + IArtifactDescriptor[] artifactDescriptors = targetRepo.getArtifactDescriptors(mirrorRequest.getArtifactKey()); + Assert.assertEquals(1, artifactDescriptors.length); + IArtifactDescriptor descriptor = artifactDescriptors[0]; + Assert.assertNull("Signatures should not be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); + Assert.assertNull("Keys should not be present", + descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); } @Override diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/TransferTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/TransferTest.java index 718db58cc..dd7b38315 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/TransferTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/repository/TransferTest.java @@ -57,7 +57,9 @@ public class TransferTest extends AbstractProvisioningTest { try { fos.close(); if (f != null) { - String[] ecfPlugins = new String[] {"org.eclipse.ecf", "org.eclipse.ecf.identity", "org.eclipse.ecf.filetransfer", "org.eclipse.ecf.provider.filetransfer", "org.eclipse.ecf.provider.filetransfer.httpclient45"}; + String[] ecfPlugins = new String[] { "org.eclipse.ecf", "org.eclipse.ecf.identity", + "org.eclipse.ecf.filetransfer", "org.eclipse.ecf.provider.filetransfer", + "org.eclipse.ecf.provider.filetransfer.httpclient5" }; StringBuilder buffer = new StringBuilder(); for (String ecfPlugin : ecfPlugins) { Bundle bundle = Platform.getBundle(ecfPlugin); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java index 973a234ff..9ac6d4b8a 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java @@ -838,4 +838,24 @@ public class DirectorApplicationTest extends AbstractProvisioningTest { return avoidTrustPromptService.getTrustInfo(untrustedChain, null); } + public void testPGPSignedArtifact() throws Exception { + File srcRepo = getTestData(null, "/testData/pgp/repoPGPOK"); + + IArtifactRepositoryManager artifactManager = getAgent().getService(IArtifactRepositoryManager.class); + IMetadataRepositoryManager metadataManager = getAgent().getService(IMetadataRepositoryManager.class); + assertNotNull(artifactManager); + assertNotNull(metadataManager); + + File destinationRepo = new File(getTempFolder(), "DirectorApp Destination"); + String[] args = getSingleRepoArgs(null, srcRepo, srcRepo, destinationRepo, "blah"); + + destinationRepo.mkdirs(); + + StringBuffer buffer = runDirectorApp(null, args); + assertFalse(buffer.toString(), buffer.toString().contains("failed")); + + artifactManager.removeRepository(srcRepo.toURI()); + metadataManager.removeRepository(srcRepo.toURI()); + delete(destinationRepo); + } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/CertificateCheckerTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/CertificateCheckerTest.java index 5f4049a5a..ba56cc6ea 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/CertificateCheckerTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/engine/CertificateCheckerTest.java @@ -16,8 +16,12 @@ package org.eclipse.equinox.p2.tests.engine; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.security.cert.Certificate; +import java.util.Comparator; import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; import org.eclipse.core.runtime.IStatus; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; import org.eclipse.equinox.internal.p2.core.AgentLocation; @@ -25,12 +29,14 @@ import org.eclipse.equinox.internal.p2.core.ProvisioningAgent; import org.eclipse.equinox.internal.p2.engine.EngineActivator; import org.eclipse.equinox.internal.p2.engine.phases.CertificateChecker; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; +import org.eclipse.equinox.internal.provisional.p2.repository.DefaultPGPPublicKeyService; import org.eclipse.equinox.p2.core.IAgentLocation; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.core.UIServices; import org.eclipse.equinox.p2.engine.IProfileRegistry; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.equinox.p2.tests.AbstractProvisioningTest; import org.eclipse.equinox.p2.tests.TestActivator; import org.eclipse.equinox.p2.tests.TestData; @@ -39,7 +45,7 @@ import org.eclipse.equinox.p2.tests.TestData; * Tests for {@link CertificateChecker}. */ public class CertificateCheckerTest extends AbstractProvisioningTest { - private static final String PGP_PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + private static final String PGP_SIGNER1_PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "\n" + "mQGNBGB1bugBDADQ5l7YnS9hNFRkBKSrvVNHt/TxeHaNNIHkdTC56I1QdThsOt4Y\n" + "oQRI27AEOaY1GFEi6+QqwxALcMMMSTgkCRs2NFGqlWMVzNYE7bJMWChVa7uQ/9CG\n" + "1HRbXwVwQx3hFgU4kmw1Kl/IH4LX76d9gAMyFANPjYZJSjbAv54wOlKruDRgpQFF\n" @@ -78,7 +84,7 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { + "cKLSMOR+Tji6n7FjOcVl6VoDKTjdxj9OgAlbZ7W9jEArrUjDdCk/m4jq9h9phpli\n" + "BHoul/nauwtlUnQes1V+39Rk9l7gddKWg3dlwg6CjB5MkmcaeyxgANcyKgrunpg=\n" + "=JYpC\n" + "-----END PGP PUBLIC KEY BLOCK-----\n"; - private static final String PGP_SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n" + "\n" + private static final String PGP_SIGNER1_SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n" + "\n" + "iQHRBAABCAA7FiEE6ZbGcKp/ZUCb9dtWE5442Q3tEfAFAmB4Bf8dHHNpZ25lcjFA\n" + "ZmFrZXVzZXIuZWNsaXBzZS5vcmcACgkQE5442Q3tEfBPuAwAhE4zA7BswKFhEtzm\n" + "DS3EbyRr/U13sV01YxqGtxYDCfrOt8TGVPXJSvo0AVP4vLFc5b+0jtVFoarFJNBu\n" @@ -88,8 +94,48 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { + "2XpuKxPXGavKfpSvssVQIZ6aWi5W6wp5lZAQQddZvYAv3Gi5CZZcUT7ayFJYdD23\n" + "p9jz76G7MXm0f0uNT9B57T72QryokUIEIJYsCb6lNjWUQB4cd0+JesM7sHwweOQ3\n" + "7iTFc+WgVJkP0e695mm1tcvtQHUPbIItYJUsndyLgGInzglxN8+F4U4k8uapydI9\n" - + "RmV2NVAifYp8z95Am5AnlG8lqjwrWk5bMbJH82QsQESrNT/h\n" + "=8Vrn\n" - + "-----END PGP SIGNATURE-----\n"; + + "RmV2NVAifYp8z95Am5AnlG8lqjwrWk5bMbJH82QsQESrNT/h\n" + "=8Vrn\n" + "-----END PGP SIGNATURE-----\n"; + + private static final String PGP_SIGNER2_PUBLIC_KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n" + "\r\n" + + "mQINBGHUyxYBEADATeNx4XA4H2fP9mD5xwlIyh7qvHLezgXpqCwNS9ATqBwnfrCV\r\n" + + "06a+pfSLsLoXrP/sdaB5WhijfuxTis18RMfoDVwGMRqyD2GiBCl2vwJDg3BUwHnc\r\n" + + "H7W6XkWKO7dkPmF+TUbD3cTWZ7cvrPmMjinmXaq8htuktGuE2VEGZRnuG1m+ChDM\r\n" + + "PnSb1ioFS2+MJv13P2fagVk2qC95DkPJGpMk3CY3ghLDEaY/KaJl+8axAlUUUk9N\r\n" + + "d3k/KVxxf5k3g26EVQkWWgH2mmolptGO101xW64iked97Cy4NK2yafOF/wmpsavx\r\n" + + "PGpOewnDgAJBBPkum6mPH0vcOZgxmLyh4uqfPfr3IaBQlbJLN2dXaDsV83j5t1wZ\r\n" + + "2qxOPcWBfORm6W7dC0TQgCXbEG0geMBpJtvnMX83Q2ORqDpjbHRJsV2k+8NxaXON\r\n" + + "pYXGr+Lj/9n0xfNEDXhCdGab0XP2tVZ5jfr2OQ5dAomEaPqK5Kq7akoWMddpDLNC\r\n" + + "G4IvH8G0cxwruJk00uwd6Nd2NVqVMRYCsBbA89VanUnutLUIpVnnOAetlX9blXHO\r\n" + + "JtmiCPGgHyp+iYGhKYVzfuZQyFhonbi0AhidJDvbHsoLT3p4Mcog1B9y6MODBE7R\r\n" + + "jwrU+qMqYsYeFhGYKbYyXv9TfEyUvtCQ/GnTtRJAQyicFdOdbK37WecS6QARAQAB\r\n" + + "tDdFY2xpcHNlIHAyIHRlc3QgU2lnbmVyIDIgPHNpZ25lcjJAZmFrZXVzZXIuZWNs\r\n" + + "aXBzZS5vcmc+iQJYBBMBCABCFiEEzZ1aK4a8T+GDlFHh4vaU9BsKs3AFAmHUyxYC\r\n" + + "GwMFCQPCZwAFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJEOL2lPQbCrNw\r\n" + + "wH4QAIiCaw1mREgt4ldz7hQvmGxdMuQwVKZPzbOIAlYbZBo0q9SmeMf/CBCO90hg\r\n" + + "LFmJmsZV4KUU5NKI7UwkDVrpUCl00Ok6+gtiUTId2tRcwXI+25I478VX27j6OkQj\r\n" + + "7Xr6giv8cn8nstt5CF6xxeqrxvpmnZC0u30jE6CL6SdXSd0vViFDPQj3KgGJCRc9\r\n" + + "St+LHB3XJTsadihzQnscqI4E2i5Z3Uj1GogqxtR59M1NAXubl5dySM0qHhwn8O+6\r\n" + + "lcgCCeuyMLLde1M1v8w07jdRUM+IFqHrRnE89EPH3MQeZbQ3UfFXK2r7wx2BJCqL\r\n" + + "Irtn68kz834ByKchGR6DqaAw0q+iF2QkgzYxpwai41BgmtUCYnj+HxQNIF4KTzDe\r\n" + + "nd8mDAPWttGCoVuV2Tyu9peYOaqyAZ2PZwUEH5MqihPCbenU17RLXzRu/IT/SH47\r\n" + + "NGrN3yKGgLZr2EVWPWFibpoxP4G4NUCHsY75uiL2EWPVSjK/+OOeHIE5k3U3lYwB\r\n" + + "7clhBwMkIhQHJ+a0SHRkKixkwrQDw4veKY4LaD0NCBLHFoV5L9orH1ToGM729kr/\r\n" + + "+4I1VQFkL3KvfLjmRbTUgwHeqEquQ96JtqowbNwlpujfHXQKDNsuiKGP5OazXll5\r\n" + + "sH2CR7e4ePqhhzxjLvi9e/79Khq+08eqllS3rs06EXEAJYTo\r\n" + "=807V\r\n" + + "-----END PGP PUBLIC KEY BLOCK-----\r\n" + ""; + private static final String PGP_SIGNER2_SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n" + "\n" + + "iQIzBAABCAAdFiEEzZ1aK4a8T+GDlFHh4vaU9BsKs3AFAmHUy4UACgkQ4vaU9BsK\n" + + "s3DjuhAAvlCtqhK/7/aAG0/cXtlpu0fPC176OmEmBGTjCsrGdWwuRHsqXbLnMBVZ\n" + + "0D1m38MDvuYZfJuP7arw7USKp+Jy54Bv/YwvHLl74YTx1BN9hAN9QvyLxLZOjdm1\n" + + "/ipiWUuUgGa/brxEZNQqSR0w17TqXkIJHeFFS3T/rNH/Ckom5vQhAEm9HwJYeGdt\n" + + "tJ5BUl7BS1rrEOs+xmzqLu6AaERREc5gGRniJe7aP2Ke+/wL6oOLG3v/6vsJSM2e\n" + + "t+Olo4Ugc6JbdNrwvTO8kkTxsi0gp2CPhKl3RZVnbGoe4tXHawmk2V3eVTa0w6iP\n" + + "ARPJ/xH2dDsRi4Kz3OkcyQOI24jGmaqpUrx3+f2BnEbcVs4cHIJc+O2gh1WUz6uY\n" + + "Zw7qtK0W3H+E7RuJLCScgasPZZPBzyA6B3o2J3bfXnG5r41aJEuiq3otgllrBakG\n" + + "u7fX00h8lylgRrlCb4mquZxxRsrl+ac6U5eYdDMkK5VNkXgrus8FedIh3vmqI7RR\n" + + "ou9GEjho4kebt1Y1yTAQnxWBtTUG2hFt6VirKydI7+ZcCZmbD9lrZT6xVQOCkyUQ\n" + + "Cwy7vNPWkpMBBRVLoaThD2+7znDpb6wNYf9mDCcCK8tyuVCDSYEFX6jlqv1yfpNl\n" + + "QlYE1biLAHl09NH397Ta/9cWdfu68I7Mv4Ev2zu45OGa83h820M=\n" + "=9Ha/\n" + "-----END PGP SIGNATURE-----\n"; class CertificateTestService extends UIServices { public boolean unsignedReturnValue = true; @@ -117,14 +163,22 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { CertificateTestService serviceUI; File unsigned; private ProvisioningAgent testAgent; + private Path agentLocation; @Override protected void setUp() throws Exception { serviceUI = new CertificateTestService(); testAgent = new ProvisioningAgent(); testAgent.registerService(UIServices.SERVICE_NAME, serviceUI); + agentLocation = Files.createTempDirectory("certificateTest"); + testAgent.setLocation(agentLocation.toUri()); testAgent.setBundleContext(TestActivator.getContext()); checker = new CertificateChecker(testAgent); + PGPPublicKeyService keyService = testAgent.getService(PGPPublicKeyService.class); + if (keyService instanceof DefaultPGPPublicKeyService) { + ((DefaultPGPPublicKeyService) keyService).setKeyServers(Set.of()); + } + try { unsigned = TestData.getFile("CertificateChecker", "unsigned.jar"); } catch (IOException e) { @@ -134,12 +188,26 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { assertTrue("1.0", unsigned.exists()); } + @Override + protected void tearDown() throws Exception { + try (Stream<Path> walk = Files.walk(agentLocation)) { + walk.sorted(Comparator.reverseOrder()).forEach(path -> { + try { + Files.delete(path); + } catch (IOException e) { + // Ignore + } + }); + } + } + /** - * Tests that installing unsigned content is not allowed when the policy says it must fail. + * Tests that installing unsigned content is not allowed when the policy says it + * must fail. */ public void testPolicyAllow() { try { - //if the service is consulted it will say no + // if the service is consulted it will say no serviceUI.unsignedReturnValue = false; System.getProperties().setProperty(EngineActivator.PROP_UNSIGNED_POLICY, EngineActivator.UNSIGNED_ALLOW); checker.add(Map.of(new ArtifactDescriptor(new ArtifactKey("what", "ever", Version.create("1"))), unsigned)); @@ -151,7 +219,8 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } /** - * Tests that installing unsigned content is not allowed when the policy says it must fail. + * Tests that installing unsigned content is not allowed when the policy says it + * must fail. */ public void testPolicyFail() { try { @@ -166,7 +235,8 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } /** - * Tests that installing unsigned content with the "prompt" policy and the prompt succeeds. + * Tests that installing unsigned content with the "prompt" policy and the + * prompt succeeds. */ public void testPolicyPromptSuccess() { try { @@ -194,7 +264,8 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } /** - * Tests that installing unsigned content with the "prompt" policy and the prompt says no. + * Tests that installing unsigned content with the "prompt" policy and the + * prompt says no. */ public void testPolicyPromptCancel() { try { @@ -210,16 +281,17 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } /** - * Tests that trust checks that occur in a headless environment are properly treated - * as permissive, but not persistent, the same way as it would be if the service registration - * were not there. + * Tests that trust checks that occur in a headless environment are properly + * treated as permissive, but not persistent, the same way as it would be if the + * service registration were not there. */ public void testBug291049() { try { // Intentionally replace our service with a null service testAgent.registerService(UIServices.SERVICE_NAME, null); checker.add(Map.of(new ArtifactDescriptor(new ArtifactKey("what", "ever", Version.create("1"))), unsigned)); - // TODO need to add some untrusted files here, too. To prove that we treated them as trusted temporarily + // TODO need to add some untrusted files here, too. To prove that we treated + // them as trusted temporarily System.getProperties().setProperty(EngineActivator.PROP_UNSIGNED_POLICY, EngineActivator.UNSIGNED_PROMPT); IStatus result = checker.start(); assertTrue("1.0", result.isOK()); @@ -241,10 +313,9 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { unsigned = TestData.getFile("pgp/repoPGPOK/plugins", "blah_1.0.0.123456.jar"); ArtifactDescriptor artifactDescriptor = new ArtifactDescriptor( new ArtifactKey("what", "ever", Version.create("1"))); - artifactDescriptor.addProperties( - Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, PGP_SIGNATURE, // - PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME, - PGP_PUBLIC_KEY)); + artifactDescriptor + .addProperties(Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, PGP_SIGNER1_SIGNATURE, // + PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME, PGP_SIGNER1_PUBLIC_KEY)); checker.add(Map.of(artifactDescriptor, unsigned)); System.getProperties().setProperty(EngineActivator.PROP_UNSIGNED_POLICY, EngineActivator.UNSIGNED_PROMPT); IStatus result = checker.start(); @@ -255,22 +326,14 @@ public class CertificateCheckerTest extends AbstractProvisioningTest { } } - public void testPGPSignedArtifactTrustedKeyInProfile() throws ProvisionException, IOException { + public void testPGPSignedArtifactTrustedKeyInProvideCapability() throws IOException { try { - // create a test profile - testAgent.registerService("FORCED_SELF", IProfileRegistry.SELF); - testAgent.registerService(IAgentLocation.SERVICE_NAME, new AgentLocation( - Files.createTempDirectory( - CertificateCheckerTest.class.getName() + "testPGPSignedArtifactTrustedKey-profile") - .toUri())); - testAgent.getService(IProfileRegistry.class).addProfile(IProfileRegistry.SELF, - Map.of(CertificateChecker.TRUSTED_KEY_STORE_PROPERTY, PGP_PUBLIC_KEY)); - unsigned = TestData.getFile("pgp/repoPGPOK/plugins", "blah_1.0.0.123456.jar"); ArtifactDescriptor artifactDescriptor = new ArtifactDescriptor( new ArtifactKey("what", "ever", Version.create("1"))); - artifactDescriptor.addProperties( - Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, PGP_SIGNATURE)); + artifactDescriptor + .addProperties(Map.of(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, PGP_SIGNER2_SIGNATURE, + PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME, PGP_SIGNER2_PUBLIC_KEY)); checker.add(Map.of(artifactDescriptor, unsigned)); System.getProperties().setProperty(EngineActivator.PROP_UNSIGNED_POLICY, EngineActivator.UNSIGNED_PROMPT); IStatus result = checker.start(); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/generator/GeneratorTests.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/generator/GeneratorTests.java index 4cd3e61c3..e09e8bc4d 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/generator/GeneratorTests.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/generator/GeneratorTests.java @@ -27,7 +27,9 @@ import org.eclipse.equinox.p2.publisher.eclipse.FeaturesAndBundlesPublisherAppli import org.eclipse.equinox.p2.publisher.eclipse.InstallPublisherApplication; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; -import org.eclipse.equinox.p2.tests.*; +import org.eclipse.equinox.p2.tests.AbstractProvisioningTest; +import org.eclipse.equinox.p2.tests.StringBufferStream; +import org.eclipse.equinox.p2.tests.TestActivator; import org.eclipse.osgi.util.NLS; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -90,7 +92,7 @@ public class GeneratorTests extends AbstractProvisioningTest { for (int i = 0; i < limit; i++) { BundleContext context = TestActivator.getContext(); Bundle bundle = context.getBundle(i); - File bundleFile = FileLocator.getBundleFile(bundle); + File bundleFile = FileLocator.getBundleFileLocation(bundle).get(); if (!bundleFile.isFile()) { //only jars please ++limit; diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/metadata/repository/MetadataRepositoryManagerTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/metadata/repository/MetadataRepositoryManagerTest.java index 22f8f9150..6ac039147 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/metadata/repository/MetadataRepositoryManagerTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/metadata/repository/MetadataRepositoryManagerTest.java @@ -387,17 +387,14 @@ public class MetadataRepositoryManagerTest extends AbstractProvisioningTest { IAgentLocation agentLocation = ServiceHelper.getService(TestActivator.getContext(), IAgentLocation.class); URI dataArea = agentLocation.getDataArea("org.eclipse.equinox.p2.repository/cache/"); File dataAreaFile = URIUtil.toFile(dataArea); - File cacheFileXML = new File(dataAreaFile, "content" + repoLocation.hashCode() + ".xml"); - File cacheFileJAR = new File(dataAreaFile, "content" + repoLocation.hashCode() + ".jar"); - File cacheFile; + File cacheFile = new File(dataAreaFile, + Integer.toString(URIUtil.append(repoLocation, "content.xml.xz").hashCode())); // as implemented in + // XZedSimpleMetadataRepository + // and CacheManager // load a remote repository and check that a local cache was created manager.loadRepository(repoLocation, null); - assertTrue("Cache file was not created.", cacheFileXML.exists() || cacheFileJAR.exists()); - if (cacheFileXML.exists()) - cacheFile = cacheFileXML; - else - cacheFile = cacheFileJAR; + assertTrue("Cache file was not created.", cacheFile.exists()); // modify the last modified date to be older than the remote file cacheFile.setLastModified(0); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/MetadataMirrorApplicationTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/MetadataMirrorApplicationTest.java index 004b5db20..94bdec524 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/MetadataMirrorApplicationTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/MetadataMirrorApplicationTest.java @@ -14,7 +14,9 @@ package org.eclipse.equinox.p2.tests.mirror; import java.io.File; -import java.net.*; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; import java.util.HashMap; import java.util.Map; import org.eclipse.equinox.internal.p2.metadata.repository.CompositeMetadataRepository; @@ -516,7 +518,7 @@ public class MetadataMirrorApplicationTest extends AbstractProvisioningTest { public void testMetadataMirrorToInvalid() { URI invalidDestRepository; try { - invalidDestRepository = new URI("http://eclipse.org/equinox/foobar/abcdefg"); + invalidDestRepository = new URI("https://eclipse.org/equinox/foobar/abcdefg"); basicRunMirrorApplication("14.1", sourceRepoLocation.toURL(), invalidDestRepository.toURL(), true); //we expect an illegal state exception to be thrown and should never get here fail("14.0 IllegalStateExpection not thrown"); @@ -535,7 +537,7 @@ public class MetadataMirrorApplicationTest extends AbstractProvisioningTest { delete(invalidRepository); try { - URI invalidDestRepository = new URI("http://eclipse.org/equinox/foobar/abcdefg"); + URI invalidDestRepository = new URI("https://eclipse.org/equinox/foobar/abcdefg"); basicRunMirrorApplication("15.1", invalidRepository.toURL(), invalidDestRepository.toURL(), true); //we expect a provisioning exception to be thrown and should never get here fail("15.0 ProvisionExpection not thrown"); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/NewMirrorApplicationMetadataTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/NewMirrorApplicationMetadataTest.java index 173bb4ca0..4d8c2ac71 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/NewMirrorApplicationMetadataTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/mirror/NewMirrorApplicationMetadataTest.java @@ -14,8 +14,12 @@ package org.eclipse.equinox.p2.tests.mirror; import java.io.File; -import java.net.*; -import java.util.*; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import org.eclipse.equinox.internal.p2.metadata.repository.CompositeMetadataRepository; import org.eclipse.equinox.internal.simpleconfigurator.utils.URIUtil; import org.eclipse.equinox.p2.core.ProvisionException; @@ -551,7 +555,7 @@ public class NewMirrorApplicationMetadataTest extends AbstractProvisioningTest { public void testMetadataMirrorToInvalid() { URI invalidDestRepository = null; try { - invalidDestRepository = new URI("http://eclipse.org/equinox/foobar/abcdefg"); + invalidDestRepository = new URI("https://eclipse.org/equinox/foobar/abcdefg"); basicRunMirrorApplication("14.1", sourceRepoLocation.toURL(), invalidDestRepository.toURL(), true); //we expect an illegal state exception to be thrown and should never get here fail("14.0 IllegalStateExpection not thrown"); @@ -574,7 +578,7 @@ public class NewMirrorApplicationMetadataTest extends AbstractProvisioningTest { delete(invalidRepository); try { - URI invalidDestRepository = new URI("http://eclipse.org/equinox/foobar/abcdefg"); + URI invalidDestRepository = new URI("https://eclipse.org/equinox/foobar/abcdefg"); basicRunMirrorApplication("15.1", invalidRepository.toURL(), invalidDestRepository.toURL(), true); //we expect a provisioning exception to be thrown and should never get here fail("15.0 ProvisionExpection not thrown"); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ANYConfigCUsActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ANYConfigCUsActionTest.java index bb92cf4ef..d6085fd8a 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ANYConfigCUsActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ANYConfigCUsActionTest.java @@ -14,13 +14,17 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.matches; +import static org.mockito.Mockito.when; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.easymock.EasyMock; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.metadata.InstallableUnit; import org.eclipse.equinox.internal.p2.publisher.eclipse.DataLoader; @@ -212,7 +216,8 @@ public class ANYConfigCUsActionTest extends ActionTest { fail("Unable to create product file advice", e); //$NON-NLS-1$ } - expect(publisherInfo.getAdvice(EasyMock.matches(configSpec), EasyMock.eq(false), (String) EasyMock.anyObject(), (Version) EasyMock.anyObject(), EasyMock.eq(IExecutableAdvice.class))).andReturn(launchingList).anyTimes(); + when(publisherInfo.getAdvice(matches(configSpec), eq(false), anyString(), any(Version.class), + eq(IExecutableAdvice.class))).thenReturn(launchingList); //configure IConfigAdvice ConfigData configData = loader.getConfigData(); @@ -220,14 +225,15 @@ public class ANYConfigCUsActionTest extends ActionTest { ArrayList<IConfigAdvice> configList = new ArrayList<>(); configList.add(configAdvice); configList.add(productAdvice); - expect(publisherInfo.getAdvice(EasyMock.matches(configSpec), EasyMock.eq(false), (String) EasyMock.anyObject(), (Version) EasyMock.anyObject(), EasyMock.eq(IConfigAdvice.class))).andReturn(configList).anyTimes(); + when(publisherInfo.getAdvice(matches(configSpec), eq(false), anyString(), any(Version.class), + eq(IConfigAdvice.class))).thenReturn(configList); //setup metadata repository IInstallableUnit[] ius = {mockIU("foo", null), mockIU("bar", null)}; //$NON-NLS-1$ //$NON-NLS-2$ metadataRepo = new TestMetadataRepository(getAgent(), ius); - expect(publisherInfo.getMetadataRepository()).andReturn(metadataRepo).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(null).anyTimes(); + when(publisherInfo.getMetadataRepository()).thenReturn(metadataRepo); + when(publisherInfo.getContextMetadataRepository()).thenReturn(null); } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AccumulateConfigDataActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AccumulateConfigDataActionTest.java index 480303b82..ffe6009d5 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AccumulateConfigDataActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AccumulateConfigDataActionTest.java @@ -15,21 +15,25 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; +import static org.mockito.Mockito.verify; + import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Map; -import org.easymock.Capture; -import org.easymock.EasyMock; +import java.util.stream.Collectors; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.equinox.frameworkadmin.BundleInfo; import org.eclipse.equinox.internal.provisional.frameworkadmin.ConfigData; +import org.eclipse.equinox.p2.publisher.IPublisherAdvice; import org.eclipse.equinox.p2.publisher.eclipse.AccumulateConfigDataAction; import org.eclipse.equinox.p2.publisher.eclipse.ConfigAdvice; import org.eclipse.equinox.p2.publisher.eclipse.LaunchingAdvice; import org.eclipse.equinox.p2.tests.TestActivator; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; public class AccumulateConfigDataActionTest extends ActionTest { @@ -43,13 +47,11 @@ public class AccumulateConfigDataActionTest extends ActionTest { private static String launcherName = "launcherName"; //$NON-NLS-1$ private static String launcherVersion = "0.0.0"; //$NON-NLS-1$ - Capture<ConfigAdvice> configAdviceCapture; - Capture<LaunchingAdvice> launchingAdviceCapture; + ArgumentCaptor<IPublisherAdvice> capture; @Override public void setUp() throws Exception { - configAdviceCapture = Capture.newInstance(); - launchingAdviceCapture = Capture.newInstance(); + capture = ArgumentCaptor.forClass(IPublisherAdvice.class); setupPublisherInfo(); setupPublisherResult(); testAction = new AccumulateConfigDataAction(publisherInfo, configSpec, configLocation, executableLocation); @@ -57,13 +59,15 @@ public class AccumulateConfigDataActionTest extends ActionTest { public void testAccumulateConfigDataAction() throws Exception { testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor()); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(capture.capture()); verifyConfigAdvice(); verifyLaunchAdvice(); debug("Completed AccumulateConfigDataActionTest."); //$NON-NLS-1$ } private void verifyLaunchAdvice() throws URISyntaxException { - LaunchingAdvice captured = launchingAdviceCapture.getValue(); + LaunchingAdvice captured = (LaunchingAdvice) capture.getAllValues().stream() + .filter(LaunchingAdvice.class::isInstance).collect(Collectors.toList()).get(0); String[] programArgs = captured.getProgramArguments(); assertTrue(programArgs.length == 4); assertTrue(programArgs[0].equalsIgnoreCase("-startup")); //$NON-NLS-1$ @@ -81,7 +85,8 @@ public class AccumulateConfigDataActionTest extends ActionTest { } private void verifyConfigAdvice() throws Exception { - ConfigAdvice captured = configAdviceCapture.getValue(); + ConfigAdvice captured = (ConfigAdvice) capture.getAllValues().stream().filter(ConfigAdvice.class::isInstance) + .collect(Collectors.toList()).get(0); Map<String, String> prop = captured.getProperties(); assertTrue(prop.get("eclipse.buildId").equalsIgnoreCase("TEST-ID")); //$NON-NLS-1$ //$NON-NLS-2$ assertTrue(prop.get("eclipse.p2.profile").equalsIgnoreCase("PlatformProfile")); //$NON-NLS-1$//$NON-NLS-2$ @@ -109,8 +114,5 @@ public class AccumulateConfigDataActionTest extends ActionTest { ConfigAdvice configAdvice = new ConfigAdvice(configData, configSpec); ArrayList<ConfigAdvice> configList = new ArrayList<>(); configList.add(configAdvice); - - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(ConfigAdvice.class), EasyMock.capture(configAdviceCapture))); - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(LaunchingAdvice.class), EasyMock.capture(launchingAdviceCapture))); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ActionTest.java index 1741c7878..25290d337 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ActionTest.java @@ -14,10 +14,9 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.io.File; @@ -118,9 +117,9 @@ public abstract class ActionTest extends AbstractProvisioningTest { for (IRequirement act : actual) { if (expected.getMatches().equals(act.getMatches())) { String descr = "IRequirement " + expected.getMatches(); - Assert.assertEquals("Min of " + descr, expected.getMin(), act.getMin()); - Assert.assertEquals("Max of " + descr, expected.getMax(), act.getMax()); - Assert.assertEquals("Greedy of " + descr, expected.isGreedy(), act.isGreedy()); + assertEquals("Min of " + descr, expected.getMin(), act.getMin()); + assertEquals("Max of " + descr, expected.getMax(), act.getMax()); + assertEquals("Greedy of " + descr, expected.isGreedy(), act.isGreedy()); return; } } @@ -128,13 +127,12 @@ public abstract class ActionTest extends AbstractProvisioningTest { } protected IInstallableUnit mockIU(String id, Version version) { - IInstallableUnit result = createMock(IInstallableUnit.class); - expect(result.getId()).andReturn(id).anyTimes(); + IInstallableUnit result = mock(IInstallableUnit.class); + when(result.getId()).thenReturn(id); if (version == null) version = Version.emptyVersion; - expect(result.getVersion()).andReturn(version).anyTimes(); - expect(result.getFilter()).andReturn(null).anyTimes(); - replay(result); + when(result.getVersion()).thenReturn(version); + when(result.getFilter()).thenReturn(null); return result; } @@ -170,31 +168,20 @@ public abstract class ActionTest extends AbstractProvisioningTest { * Call this method to setup Publisher Info, not <code>insertPublisherInfoBehavior</code> */ public void setupPublisherInfo() { - publisherInfo = createPublisherInfoMock(); + publisherInfo = mock(IPublisherInfo.class); String[] config = getArrayFromString(configSpec, COMMA_SEPARATOR); - expect(publisherInfo.getConfigurations()).andReturn(config).anyTimes(); + when(publisherInfo.getConfigurations()).thenReturn(config); insertPublisherInfoBehavior(); - replay(publisherInfo); - } - - /** - * Creates the mock object for the IPublisherInfo. Subclasses - * can override to create a nice or strict mock instead. - * @return The publisher info mock - * @see org.easymock.EasyMock#createNiceMock(Class) - * @see org.easymock.EasyMock#createStrictMock(Class) - */ - protected IPublisherInfo createPublisherInfoMock() { - return createMock(IPublisherInfo.class); } /** * Do not call this method, it is called by <code>setupPublisherInfo</code>. */ protected void insertPublisherInfoBehavior() { - expect(publisherInfo.getMetadataRepository()).andReturn(createTestMetdataRepository(new IInstallableUnit[0])).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(createTestMetdataRepository(new IInstallableUnit[0])).anyTimes(); + when(publisherInfo.getMetadataRepository()).thenReturn(createTestMetdataRepository(new IInstallableUnit[0])); + when(publisherInfo.getContextMetadataRepository()) + .thenReturn(createTestMetdataRepository(new IInstallableUnit[0])); } public void cleanup() { diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AdviceMatcher.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AdviceMatcher.java deleted file mode 100644 index f23e1f53b..000000000 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/AdviceMatcher.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2017 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.equinox.p2.tests.publisher.actions; - -import org.easymock.EasyMock; -import org.easymock.IArgumentMatcher; -import org.eclipse.equinox.p2.metadata.Version; -import org.eclipse.equinox.p2.publisher.IPublisherAdvice; - -/** - * A matcher that matches advice applicable to a given id and version. - */ -public class AdviceMatcher implements IArgumentMatcher { - private final Version version; - private final String id; - private static Class<?> clazz; - - public static IPublisherAdvice adviceMatches(String id, Version version, Class<?> clazz) { - AdviceMatcher.clazz = clazz; - EasyMock.reportMatcher(new AdviceMatcher(id, version)); - return null; - } - - public AdviceMatcher(String id, Version version) { - this.id = id; - this.version = version; - } - - @Override - public void appendTo(StringBuffer buf) { - buf.append("AdviceMatcher[" + id + ',' + version + ']'); - } - - @Override - public boolean matches(Object arg) { - if (!(arg instanceof IPublisherAdvice)) - return false; - if (!(clazz.isAssignableFrom(arg.getClass()))) - return false; - IPublisherAdvice advice = (IPublisherAdvice) arg; - return advice.isApplicable("", false, id, version); - } -} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/BundlesActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/BundlesActionTest.java index 72bde8105..fd95c834b 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/BundlesActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/BundlesActionTest.java @@ -16,15 +16,16 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.and; -import static org.easymock.EasyMock.capture; -import static org.easymock.EasyMock.expect; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.errorStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.statusWithMessageWhich; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.io.BufferedInputStream; import java.io.File; @@ -39,9 +40,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.zip.ZipInputStream; -import org.easymock.Capture; -import org.easymock.CaptureType; -import org.easymock.EasyMock; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; @@ -60,6 +58,7 @@ import org.eclipse.equinox.p2.metadata.IUpdateDescriptor; import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.metadata.VersionRange; +import org.eclipse.equinox.p2.metadata.expression.IMatchExpression; import org.eclipse.equinox.p2.publisher.AbstractPublisherApplication; import org.eclipse.equinox.p2.publisher.AdviceFileAdvice; import org.eclipse.equinox.p2.publisher.IPublisherInfo; @@ -80,6 +79,8 @@ import org.eclipse.equinox.p2.tests.TestActivator; import org.eclipse.equinox.p2.tests.TestData; import org.eclipse.equinox.p2.tests.publisher.TestArtifactRepository; import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; public class BundlesActionTest extends ActionTest { private static final String OSGI = PublisherHelper.OSGI_BUNDLE_CLASSIFIER; @@ -143,39 +144,27 @@ public class BundlesActionTest extends ActionTest { private static final VersionRange TEST1_IU_D_VERSION_RANGE = VersionRange.create("1.3.0");//$NON-NLS-1$ protected TestArtifactRepository artifactRepository = new TestArtifactRepository(getAgent()); - - private Capture<ITouchpointAdvice> tpAdvice1, tpAdvice2; - private Capture<IUpdateDescriptorAdvice> udAdvice3; - private Capture<ICapabilityAdvice> capAdvice5; - - @Override - public void setupPublisherInfo() { - tpAdvice1 = Capture.newInstance(CaptureType.ALL); - tpAdvice2 = Capture.newInstance(CaptureType.ALL); - - udAdvice3 = Capture.newInstance(CaptureType.ALL); - capAdvice5 = Capture.newInstance(CaptureType.ALL); - - super.setupPublisherInfo(); - } + private ArgumentCaptor<AdviceFileAdvice> updateDescriptorCapture; public void testAll() throws Exception { File[] files = TEST_BASE.listFiles(); - testAction = new BundlesAction(files); + testAction = Mockito.spy(new BundlesAction(files)); setupPublisherResult(); setupPublisherInfo(); artifactRepository.setProperty(AbstractPublisherApplication.PUBLISH_PACK_FILES_AS_SIBLINGS, "true");//$NON-NLS-1$ + updateDescriptorCapture = ArgumentCaptor.forClass(AdviceFileAdvice.class); + IStatus status = testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor()); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(updateDescriptorCapture.capture()); + assertEquals(Status.OK_STATUS, status); - assertEquals(Status.OK_STATUS, testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor())); verifyBundlesAction(); cleanup(); - debug("Completed BundlesActionTest.");//$NON-NLS-1$ } public void testTranslationFragment() { File foo_fragment = new File(TestActivator.getTestDataFolder(), "FragmentPublisherTest/foo.fragment");//$NON-NLS-1$ File foo = new File(TestActivator.getTestDataFolder(), "FragmentPublisherTest/foo");//$NON-NLS-1$ - BundlesAction bundlesAction = new BundlesAction(new File[] {foo_fragment}); + BundlesAction bundlesAction = new BundlesAction(new File[] { foo_fragment }); PublisherInfo info = new PublisherInfo(); PublisherResult results = new PublisherResult(); @@ -185,7 +174,7 @@ public class BundlesActionTest extends ActionTest { info = new PublisherInfo(); results = new PublisherResult(); - bundlesAction = new BundlesAction(new File[] {foo}); + bundlesAction = new BundlesAction(new File[] { foo }); bundlesAction.perform(info, results, new NullProgressMonitor()); ius = results.getIUs(null, null); assertEquals(1, ius.size()); @@ -197,7 +186,7 @@ public class BundlesActionTest extends ActionTest { utils.setTranslationSource(queryableArray); assertEquals("English Foo", utils.getIUProperty(iu, IInstallableUnit.PROP_NAME)); - bundlesAction = new BundlesAction(new File[] {foo_fragment}); + bundlesAction = new BundlesAction(new File[] { foo_fragment }); bundlesAction.perform(info, results, new NullProgressMonitor()); ius = results.getIUs(null, null); assertEquals(3, ius.size()); @@ -238,7 +227,8 @@ public class BundlesActionTest extends ActionTest { canonicalIdx = 0; } - try (ZipInputStream actual = artifactRepository.getZipInputStream(descriptors[canonicalIdx]); ZipInputStream expected = new ZipInputStream(new FileInputStream(TEST_FILE2))) { + try (ZipInputStream actual = artifactRepository.getZipInputStream(descriptors[canonicalIdx]); + ZipInputStream expected = new ZipInputStream(new FileInputStream(TEST_FILE2))) { TestData.assertEquals(expected, actual); } @@ -248,12 +238,14 @@ public class BundlesActionTest extends ActionTest { IArtifactKey key1 = ArtifactKey.parse("osgi.bundle,test1,0.1.0");//$NON-NLS-1$ ZipInputStream zis = artifactRepository.getZipInputStream(key1); - Map<String, Object[]> fileMap = getFileMap(new HashMap<>(), new File[] {TEST_FILE1}, new Path(TEST_FILE1.getAbsolutePath())); + Map<String, Object[]> fileMap = getFileMap(new HashMap<>(), new File[] { TEST_FILE1 }, + new Path(TEST_FILE1.getAbsolutePath())); TestData.assertContains(fileMap, zis, true); } private void verifyBundle1() { - List<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST1_PROVBUNDLE_NAME, IPublisherResult.ROOT)); + List<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST1_PROVBUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); IInstallableUnit bundle1IU = ius.get(0); @@ -272,25 +264,28 @@ public class BundlesActionTest extends ActionTest { verifyProvidedCapability(providedCapabilities, OSGI_IDENTITY, TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION); verifyProvidedCapability(providedCapabilities, OSGI, TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION); verifyProvidedCapability(providedCapabilities, TEST1_PROV_Z_NAMESPACE, TEST1_PROVZ_NAME, TEST2_PROVZ_VERSION); - verifyProvidedCapability(providedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "source", Version.create("1.0.0"));//$NON-NLS-1$//$NON-NLS-2$ + verifyProvidedCapability(providedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "source", //$NON-NLS-1$ + Version.create("1.0.0"));//$NON-NLS-1$ assertEquals(5, providedCapabilities.size()); - Collection<ITouchpointData> data = bundle1IU.getTouchpointData(); + List<AdviceFileAdvice> tData = updateDescriptorCapture.getAllValues(); boolean found = false; - for (ITouchpointData td : data) { - ITouchpointInstruction configure = td.getInstruction("configure"); - if (configure == null) - continue; - String body = configure.getBody(); - if (body != null && body.indexOf("download.eclipse.org/releases/ganymede") > 0) { - found = true; + for (AdviceFileAdvice iTouchpointAdvice : tData) { + ITouchpointInstruction configure = iTouchpointAdvice.getTouchpointData(NO_TP_DATA) + .getInstruction("configure"); + if (configure != null) { + String body = configure.getBody(); + if (body != null && body.indexOf("download.eclipse.org/releases/ganymede") > 0) { + found = true; + } } } assertTrue(found); } private void verifyBundle2() { - List<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST2_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); + List<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST2_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); IInstallableUnit bundleIu = ius.get(0); @@ -307,14 +302,16 @@ public class BundlesActionTest extends ActionTest { // check provided capabilities Collection<IProvidedCapability> providedCapabilities = bundleIu.getProvidedCapabilities(); - verifyProvidedCapability(providedCapabilities, PROVBUNDLE_NAMESPACE, TEST2_PROV_BUNDLE_NAME, PROVBUNDLE2_VERSION); + verifyProvidedCapability(providedCapabilities, PROVBUNDLE_NAMESPACE, TEST2_PROV_BUNDLE_NAME, + PROVBUNDLE2_VERSION); verifyProvidedCapability(providedCapabilities, OSGI, TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION); verifyProvidedCapability(providedCapabilities, OSGI_IDENTITY, TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION); verifyProvidedCapability(providedCapabilities, TEST2_PROV_Z_NAMESPACE, TEST2_PROV_Z_NAME, TEST2_PROVZ_VERSION); verifyProvidedCapability(providedCapabilities, TEST2_PROV_Y_NAMESPACE, TEST2_PROV_Y_NAME, TEST2_PROVY_VERSION); verifyProvidedCapability(providedCapabilities, TEST2_PROV_X_NAMESPACE, TEST2_PROV_X_NAME, TEST2_PROVX_VERSION); - verifyProvidedCapability(providedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "bundle", Version.create("1.0.0"));//$NON-NLS-1$//$NON-NLS-2$ - assertEquals(7, providedCapabilities.size()); /*number of tested elements*/ + verifyProvidedCapability(providedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "bundle", //$NON-NLS-1$ + Version.create("1.0.0"));//$NON-NLS-1$ + assertEquals(7, providedCapabilities.size()); /* number of tested elements */ // check %bundle name is correct Map<String, String> prop = bundleIu.getProperties(); @@ -336,27 +333,41 @@ public class BundlesActionTest extends ActionTest { } private void verifyBundle3() { - // also a regression test for bug 393051: manifest headers use uncommon (but valid) capitalization - ArrayList<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST3_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); + // also a regression test for bug 393051: manifest headers use uncommon (but + // valid) capitalization + ArrayList<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST3_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); - - IInstallableUnit bundleIu = ius.get(0); - - IUpdateDescriptor updateDescriptor = bundleIu.getUpdateDescriptor(); - String name = RequiredCapability.extractName(updateDescriptor.getIUsBeingUpdated().iterator().next()); + IUpdateDescriptor updateDescriptor = null; + boolean found = false; + for (AdviceFileAdvice advice : updateDescriptorCapture.getAllValues()) { + IUpdateDescriptor descriptor = advice.getUpdateDescriptor(new InstallableUnitDescription()); + if (descriptor != null) { + Collection<IMatchExpression<IInstallableUnit>> iUsBeingUpdated = descriptor.getIUsBeingUpdated(); + if (iUsBeingUpdated != null) { + String name = RequiredCapability.extractName(descriptor.getIUsBeingUpdated().iterator().next()); + if (TEST3_PROV_BUNDLE_NAME.equals(name)) { + updateDescriptor = descriptor; + found = true; + break; + } + } + } + } + assertTrue(found); VersionRange range = RequiredCapability.extractRange(updateDescriptor.getIUsBeingUpdated().iterator().next()); String description = updateDescriptor.getDescription(); int severity = updateDescriptor.getSeverity(); VersionRange expectedRange = new VersionRange("(0.0.1," + BUNDLE3_VERSION + "]"); - assertEquals(TEST3_PROV_BUNDLE_NAME, name); assertEquals(expectedRange, range); assertEquals("Some description about this update", description.trim()); assertEquals(8, severity); } private void verifyBundle4() { - ArrayList<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST4_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); + ArrayList<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST4_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); IInstallableUnit bundleIu = ius.get(0); @@ -365,23 +376,29 @@ public class BundlesActionTest extends ActionTest { // check required capabilities Collection<IRequirement> requirements = bundleIu.getRequirements(); - verifyRequirement(requirements, JAVA_PACKAGE, TEST4_REQ_PACKAGE_OPTIONAL_NAME, DEFAULT_VERSION_RANGE, null, 0, 1, false); - verifyRequirement(requirements, JAVA_PACKAGE, TEST4_REQ_PACKAGE_OPTGREEDY_NAME, DEFAULT_VERSION_RANGE, null, 0, 1, true); + verifyRequirement(requirements, JAVA_PACKAGE, TEST4_REQ_PACKAGE_OPTIONAL_NAME, DEFAULT_VERSION_RANGE, null, 0, + 1, false); + verifyRequirement(requirements, JAVA_PACKAGE, TEST4_REQ_PACKAGE_OPTGREEDY_NAME, DEFAULT_VERSION_RANGE, null, 0, + 1, true); verifyRequirement(requirements, OSGI, TEST4_REQ_BUNDLE_OPTIONAL_NAME, DEFAULT_VERSION_RANGE, null, 0, 1, false); verifyRequirement(requirements, OSGI, TEST4_REQ_BUNDLE_OPTGREEDY_NAME, DEFAULT_VERSION_RANGE, null, 0, 1, true); assertEquals(4, requirements.size()); } private void verifyBundle5() { - ArrayList<IInstallableUnit> ius = new ArrayList<>(publisherResult.getIUs(TEST5_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); + ArrayList<IInstallableUnit> ius = new ArrayList<>( + publisherResult.getIUs(TEST5_PROV_BUNDLE_NAME, IPublisherResult.ROOT)); assertEquals(1, ius.size()); IInstallableUnit bundle5IU = ius.get(0); - + for (AdviceFileAdvice adv : updateDescriptorCapture.getAllValues()) { + IRequirement[] reqs = adv.getRequiredCapabilities(new InstallableUnitDescription()); + if (reqs != null) { + verifyRequirement(List.of(reqs), "bar", "foo", VersionRange.emptyRange, null, 6, 7, true); + } + } Collection<IRequirement> requirements = bundle5IU.getRequirements(); verifyRequirement(requirements, OSGI_EE, TEST5_REQ_EE, null, 1, 1, true); - verifyRequirement(requirements, "bar", "foo", VersionRange.emptyRange, null, 6, 7, true); - assertEquals(2, requirements.size()); } @Override @@ -395,7 +412,8 @@ public class BundlesActionTest extends ActionTest { @Override protected void insertPublisherInfoBehavior() { - //super sets publisherInfo.getMetadataRepository and publisherInfo.getContextMetadataRepository + // super sets publisherInfo.getMetadataRepository and + // publisherInfo.getContextMetadataRepository super.insertPublisherInfoBehavior(); Map<String, String> sarProperties = new HashMap<>(); sarProperties.put("key1", "value1");//$NON-NLS-1$//$NON-NLS-2$ @@ -405,23 +423,21 @@ public class BundlesActionTest extends ActionTest { sdkProperties.put("key1", "value1");//$NON-NLS-1$//$NON-NLS-2$ sdkProperties.put("key2", "value2");//$NON-NLS-1$//$NON-NLS-2$ - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getAdvice(null, false, null, null, ICapabilityAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()) + .thenReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH); + when(publisherInfo.getAdvice(null, false, null, null, ICapabilityAdvice.class)).thenReturn(new ArrayList<>()); expectOtherAdviceQueries(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION); expectPropertyAdviceQuery(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION, sarProperties); expectUpdateDescriptorAdviceQuery(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION, null); - expectTouchpointAdviceQuery(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION, tpAdvice1.getValues()); expectOtherAdviceQueries(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION); expectPropertyAdviceQuery(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION, sdkProperties); expectUpdateDescriptorAdviceQuery(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION, null); - expectTouchpointAdviceQuery(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION, tpAdvice2.getValues()); expectOtherAdviceQueries(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION); expectPropertyAdviceQuery(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION, sarProperties); - expectUpdateDescriptorAdviceQuery(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION, udAdvice3.getValues()); expectTouchpointAdviceQuery(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION, null); expectOtherAdviceQueries(TEST4_PROV_BUNDLE_NAME, BUNDLE4_VERSION); @@ -429,49 +445,43 @@ public class BundlesActionTest extends ActionTest { expectUpdateDescriptorAdviceQuery(TEST4_PROV_BUNDLE_NAME, BUNDLE4_VERSION, null); expectTouchpointAdviceQuery(TEST4_PROV_BUNDLE_NAME, BUNDLE4_VERSION, null); - expectCapabilityAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, capAdvice5.getValues()); +// expectCapabilityAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, capAdvice5.getValues()); expectOtherAdviceQueries(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION); expectPropertyAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, sarProperties); expectUpdateDescriptorAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, null); expectTouchpointAdviceQuery(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, null); - //capture any touchpoint advice, and return the captured advice when the action asks for it - publisherInfo.addAdvice(and(AdviceMatcher.adviceMatches(TEST1_PROVBUNDLE_NAME, BUNDLE1_VERSION, ITouchpointAdvice.class), capture(tpAdvice1))); - EasyMock.expectLastCall().anyTimes(); - - publisherInfo.addAdvice(and(AdviceMatcher.adviceMatches(TEST2_PROV_BUNDLE_NAME, BUNDLE2_VERSION, ITouchpointAdvice.class), capture(tpAdvice2))); - EasyMock.expectLastCall().anyTimes(); - - publisherInfo.addAdvice(and(AdviceMatcher.adviceMatches(TEST3_PROV_BUNDLE_NAME, BUNDLE3_VERSION, AdviceFileAdvice.class), capture(udAdvice3))); - - publisherInfo.addAdvice(and(AdviceMatcher.adviceMatches(TEST5_PROV_BUNDLE_NAME, BUNDLE5_VERSION, AdviceFileAdvice.class), capture(capAdvice5))); - EasyMock.expectLastCall().anyTimes(); } private void expectOtherAdviceQueries(String bundleName, Version bundleVersion) { - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ICapabilityAdvice.class)) - .andReturn(Collections.emptyList()); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IAdditionalInstallableUnitAdvice.class)) - .andReturn(Collections.emptyList()); - expect(publisherInfo.getAdvice(null, true, bundleName, bundleVersion, IBundleShapeAdvice.class)).andReturn(null); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ICapabilityAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IAdditionalInstallableUnitAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, true, bundleName, bundleVersion, IBundleShapeAdvice.class)).thenReturn(null); } - private void expectCapabilityAdviceQuery(String bundleName, Version bundleVersion, Collection<ICapabilityAdvice> answer) { - if (answer == null) - answer = Collections.emptyList(); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ICapabilityAdvice.class)).andReturn(answer); - } +// private void expectCapabilityAdviceQuery(String bundleName, Version bundleVersion, +// Collection<ICapabilityAdvice> answer) { +// if (answer == null) +// answer = Collections.emptyList(); +// when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ICapabilityAdvice.class)) +// .thenReturn(answer); +// } - private void expectUpdateDescriptorAdviceQuery(String bundleName, Version bundleVersion, Collection<IUpdateDescriptorAdvice> answer) { + private void expectUpdateDescriptorAdviceQuery(String bundleName, Version bundleVersion, + Collection<IUpdateDescriptorAdvice> answer) { if (answer == null) answer = Collections.emptyList(); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IUpdateDescriptorAdvice.class)).andReturn(answer); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IUpdateDescriptorAdvice.class)) + .thenReturn(answer); } - private void expectTouchpointAdviceQuery(String bundleName, Version bundleVersion, Collection<ITouchpointAdvice> answer) { + private void expectTouchpointAdviceQuery(String bundleName, Version bundleVersion, List<ITouchpointAdvice> answer) { if (answer == null) answer = Collections.emptyList(); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ITouchpointAdvice.class)).andReturn(answer).anyTimes(); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, ITouchpointAdvice.class)) + .thenReturn(answer); } private void expectPropertyAdviceQuery(String bundleName, Version bundleVersion, Map<String, String> answer) { @@ -480,20 +490,22 @@ public class BundlesActionTest extends ActionTest { propertyAdvices = Collections.singletonList(createPropertyAdvice(answer)); else propertyAdvices = Collections.emptyList(); - expect(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IPropertyAdvice.class)).andReturn(propertyAdvices).times(2); + when(publisherInfo.getAdvice(null, false, bundleName, bundleVersion, IPropertyAdvice.class)) + .thenReturn(propertyAdvices); } private IPropertyAdvice createPropertyAdvice(Map<String, String> properties) { - IPropertyAdvice mockAdvice = EasyMock.createMock(IPropertyAdvice.class); - expect(mockAdvice.getInstallableUnitProperties((InstallableUnitDescription) EasyMock.anyObject())).andReturn(null).anyTimes(); - expect(mockAdvice.getArtifactProperties((IInstallableUnit) EasyMock.anyObject(), (IArtifactDescriptor) EasyMock.anyObject())).andReturn(properties).anyTimes(); - EasyMock.replay(mockAdvice); + IPropertyAdvice mockAdvice = mock(IPropertyAdvice.class); + when(mockAdvice.getInstallableUnitProperties(any(InstallableUnitDescription.class))).thenReturn(null); + when(mockAdvice.getArtifactProperties(any(IInstallableUnit.class), any(IArtifactDescriptor.class))) + .thenReturn(properties); return mockAdvice; } public void testDynamicImport() throws Exception { File testData = getTestData("dymamicImport", "testData/dynamicImport"); - IInstallableUnit iu = BundlesAction.createBundleIU(BundlesAction.createBundleDescription(testData), null, new PublisherInfo()); + IInstallableUnit iu = BundlesAction.createBundleIU(BundlesAction.createBundleDescription(testData), null, + new PublisherInfo()); Collection<IRequirement> requirements = iu.getRequirements(); verifyRequirement(requirements, OSGI_EE, TESTDYN_REQ_EE, null, 1, 1, true); @@ -510,11 +522,13 @@ public class BundlesActionTest extends ActionTest { // overall status shall be error... assertThat(status, errorStatus()); List<IStatus> childStatuses = Arrays.asList(status.getChildren()); - assertThat(childStatuses, hasItem(statusWithMessageWhich(containsString("The manifest line \"foo\" is invalid; it has no colon ':' character after the header key.")))); + assertThat(childStatuses, hasItem(statusWithMessageWhich(containsString( + "The manifest line \"foo\" is invalid; it has no colon ':' character after the header key.")))); assertThat(childStatuses.size(), is(1)); // ... but the valid bundle must still be published - Collection<IInstallableUnit> ius = publisherResult.getIUs("org.eclipse.p2.test.validManifest", IPublisherResult.ROOT); + Collection<IInstallableUnit> ius = publisherResult.getIUs("org.eclipse.p2.test.validManifest", + IPublisherResult.ROOT); assertThat(ius.size(), is(1)); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/CaptureList.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/CaptureList.java deleted file mode 100644 index e317d621c..000000000 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/CaptureList.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2017 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.equinox.p2.tests.publisher.actions; - -import java.util.AbstractList; -import org.easymock.Capture; - -/** - * An object that adapts an EasyMock Capture to a List. - */ -public class CaptureList<E> extends AbstractList<E> { - private Capture<E> capture; - - public CaptureList(Capture<E> capture) { - this.capture = capture; - } - - @Override - public E get(int arg0) { - return capture.getValue(); - } - - @Override - public int size() { - return capture.hasCaptured() ? 1 : 0; - } - -} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ConfigCUsActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ConfigCUsActionTest.java index 24b6997bb..99afa871c 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ConfigCUsActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ConfigCUsActionTest.java @@ -15,13 +15,17 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.matches; +import static org.mockito.Mockito.when; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.easymock.EasyMock; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.metadata.InstallableUnit; import org.eclipse.equinox.internal.p2.publisher.eclipse.DataLoader; @@ -147,7 +151,8 @@ public class ConfigCUsActionTest extends ActionTest { ConfigAdvice configAdvice = new ConfigAdvice(configData, configSpec); ArrayList<IConfigAdvice> configList = new ArrayList<>(); configList.add(configAdvice); - expect(publisherInfo.getAdvice(EasyMock.matches(configSpec), EasyMock.eq(false), (String) EasyMock.anyObject(), (Version) EasyMock.anyObject(), EasyMock.eq(IConfigAdvice.class))).andReturn(configList).anyTimes(); + when(publisherInfo.getAdvice(matches(configSpec), eq(false), anyString(), any(Version.class), + eq(IConfigAdvice.class))).thenReturn(configList); //configure IExecutableAdvice LauncherData launcherData = loader.getLauncherData(); @@ -164,14 +169,15 @@ public class ConfigCUsActionTest extends ActionTest { // TODO Auto-generated catch block } - expect(publisherInfo.getAdvice(EasyMock.matches(configSpec), EasyMock.eq(false), (String) EasyMock.anyObject(), (Version) EasyMock.anyObject(), EasyMock.eq(IExecutableAdvice.class))).andReturn(launchingList).anyTimes(); + when(publisherInfo.getAdvice(matches(configSpec), eq(false), anyString(), any(Version.class), + eq(IExecutableAdvice.class))).thenReturn(launchingList); //setup metadata repository IInstallableUnit[] ius = {mockIU("foo", null), mockIU("bar", null)}; //$NON-NLS-1$ //$NON-NLS-2$ metadataRepo = new TestMetadataRepository(getAgent(), ius); - expect(publisherInfo.getMetadataRepository()).andReturn(metadataRepo).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(null).anyTimes(); + when(publisherInfo.getMetadataRepository()).thenReturn(metadataRepo); + when(publisherInfo.getContextMetadataRepository()).thenReturn(null); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxExecutableActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxExecutableActionTest.java index 41bf705a0..532bab05e 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxExecutableActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxExecutableActionTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2017 Code 9 and others. + * Copyright (c) 2008, 2021 Code 9 and others. * * This * program and the accompanying materials are made available under the terms of @@ -15,9 +15,11 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.anyBoolean; -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.expect; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.when; import java.io.File; import java.io.FileInputStream; @@ -30,6 +32,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.LinkedList; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; @@ -62,10 +65,9 @@ public class EquinoxExecutableActionTest extends ActionTest { private static final File LINUX_EXEC = new File(TestActivator.getTestDataFolder(), "EquinoxExecutableActionTest/linux/"); //$NON-NLS-1$ private static final File WIN_EXEC = new File(TestActivator.getTestDataFolder(), "EquinoxExecutableActionTest/win/"); //$NON-NLS-1$ private final String EXECUTABLE_NAME = "LauncherName"; //$NON-NLS-1$ - private Collection<IBrandingAdvice> brandingAdvice = new LinkedList<>(); private String macConfigCocoa = "cocoa.macosx.x86"; //$NON-NLS-1$ - private String winConfig = "win32.win32.x86"; //$NON-NLS-1$ - private String linuxConfig = "linux.gtk.x86"; //$NON-NLS-1$ + private String winConfig = "win32.win32.x86_64"; //$NON-NLS-1$ + private String linuxConfig = "linux.gtk.x86_64"; //$NON-NLS-1$ private ExecutablesDescriptor executablesDescriptor; private IArtifactRepository artifactRepository; private Version version = Version.create("1.2.3"); //$NON-NLS-1$ @@ -76,9 +78,6 @@ public class EquinoxExecutableActionTest extends ActionTest { public void setUp() throws Exception { setupPublisherInfo(); setupPublisherResult(); - } - - private void setupArtifactRepository() { artifactRepository = new TestArtifactRepository(getAgent()); } @@ -118,7 +117,10 @@ public class EquinoxExecutableActionTest extends ActionTest { private void testExecutableAction(String idBase, final String osArg, String config, File exec, File icon) { id = idBase; - setupBrandingAdvice(osArg, configSpec, exec, icon); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()).thenReturn(IPublisherInfo.A_PUBLISH); + when(publisherInfo.getAdvice(anyString(), anyBoolean(), nullable(String.class), nullable(Version.class), + eq(IBrandingAdvice.class))).then(invocation -> setupBrandingAdvice(osArg, icon)); executablesDescriptor = ExecutablesDescriptor.createDescriptor(osArg, "eclipse", exec); testAction = new EquinoxExecutableAction(executablesDescriptor, config, idBase, version, flavorArg); testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor()); @@ -128,10 +130,10 @@ public class EquinoxExecutableActionTest extends ActionTest { private void verifyResults(String idBase, String confSpec) { ArrayList<IInstallableUnit> iuList = new ArrayList<>(publisherResult.getIUs(null, IPublisherResult.ROOT)); + assertEquals(3, iuList.size()); verifyEclipseIU(iuList, idBase, confSpec); verifyCU(iuList, idBase, confSpec); verifyExecIU(iuList, idBase, confSpec); - assertTrue(iuList.size() == 3); } private void verifyCU(ArrayList<IInstallableUnit> iuList, String idBase, String confSpec) { @@ -162,16 +164,16 @@ public class EquinoxExecutableActionTest extends ActionTest { private void verifyEclipseIU(ArrayList<IInstallableUnit> iuList, String idBase, String confSpec) { for (IInstallableUnit possibleEclipse : iuList) { if (possibleEclipse.getId().equals((idBase + ".executable." + confSpec + "." + EXECUTABLE_NAME))) { //$NON-NLS-1$//$NON-NLS-2$ - assertTrue(possibleEclipse.getVersion().equals(version)); + assertEquals(version, possibleEclipse.getVersion()); Collection<IProvidedCapability> providedCapability = possibleEclipse.getProvidedCapabilities(); verifyProvidedCapability(providedCapability, IInstallableUnit.NAMESPACE_IU_ID, idBase + ".executable." + confSpec + "." + EXECUTABLE_NAME, version); //$NON-NLS-1$ //$NON-NLS-2$ - assertTrue(providedCapability.size() == 1); + assertEquals(1, providedCapability.size()); Collection<IRequirement> req = possibleEclipse.getRequirements(); - assertTrue(req.size() == 0); + assertEquals(0, req.size()); return;//pass } } - fail(); + fail("No executable installable unit."); } private void verifyExecIU(ArrayList<IInstallableUnit> iuList, String idBase, String confSpec) { @@ -274,15 +276,8 @@ public class EquinoxExecutableActionTest extends ActionTest { } } - @Override - protected void insertPublisherInfoBehavior() { - setupArtifactRepository(); - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getAdvice((String) anyObject(), anyBoolean(), (String) anyObject(), (Version) anyObject(), (Class<IBrandingAdvice>) anyObject())).andReturn(brandingAdvice); - } - - private void setupBrandingAdvice(final String osArg, final String config, final File exec, final File icon) { + private List<IBrandingAdvice> setupBrandingAdvice(final String osArg, final File icon) { + List<IBrandingAdvice> brandingAdvice = new LinkedList<>(); brandingAdvice.add(new IBrandingAdvice() { @Override public boolean isApplicable(String configSpec, boolean includeDefault, String id, Version version) { @@ -304,5 +299,6 @@ public class EquinoxExecutableActionTest extends ActionTest { return EXECUTABLE_NAME; } }); + return brandingAdvice; } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxLauncherCUActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxLauncherCUActionTest.java index 95442d175..ff0b846fb 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxLauncherCUActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/EquinoxLauncherCUActionTest.java @@ -15,9 +15,8 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Collection; @@ -56,13 +55,12 @@ public class EquinoxLauncherCUActionTest extends ActionTest { } protected IInstallableUnit mockIU(String id, Version version, boolean fragment) { - IInstallableUnit result = createMock(IInstallableUnit.class); - expect(result.getId()).andReturn(id).anyTimes(); + IInstallableUnit result = mock(IInstallableUnit.class); + when(result.getId()).thenReturn(id); if (version == null) version = Version.emptyVersion; - expect(result.getVersion()).andReturn(version).anyTimes(); - expect(result.getFilter()).andReturn(null).anyTimes(); - replay(result); + when(result.getVersion()).thenReturn(version); + when(result.getFilter()).thenReturn(null); return result; } @@ -108,7 +106,10 @@ public class EquinoxLauncherCUActionTest extends ActionTest { ArrayList<IVersionAdvice> versionList = new ArrayList<>(); versionList.add(versionAdvice); - expect(publisherInfo.getAdvice(null, true, EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER, null, IVersionAdvice.class)).andReturn(versionList); - expect(publisherInfo.getAdvice(configSpec, true, EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER + "." + configSpec, null, IVersionAdvice.class)).andReturn(versionList); //$NON-NLS-1$ + when(publisherInfo.getAdvice(null, true, EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER, null, + IVersionAdvice.class)).thenReturn(versionList); + when(publisherInfo.getAdvice(configSpec, true, + EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER + "." + configSpec, null, IVersionAdvice.class)) //$NON-NLS-1$ + .thenReturn(versionList); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/FeaturesActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/FeaturesActionTest.java index bae3a460e..279660892 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/FeaturesActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/FeaturesActionTest.java @@ -15,10 +15,10 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.and; -import static org.easymock.EasyMock.capture; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.isA; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.io.File; import java.io.IOException; @@ -26,10 +26,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.zip.ZipInputStream; -import org.easymock.Capture; -import org.easymock.EasyMock; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; @@ -62,6 +61,8 @@ import org.eclipse.equinox.p2.tests.TestData; import org.eclipse.equinox.p2.tests.TestMetadataRepository; import org.eclipse.equinox.p2.tests.publisher.TestArtifactRepository; import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; public class FeaturesActionTest extends ActionTest { @@ -75,12 +76,12 @@ public class FeaturesActionTest extends ActionTest { private Version barVersion = Version.create("1.1.1"); //$NON-NLS-1$ private String BAR = "bar"; //$NON-NLS-1$ private String FOO = "foo"; //$NON-NLS-1$ - private Capture<ITouchpointAdvice> tpAdvice; + private ArgumentCaptor<ITouchpointAdvice> capture = ArgumentCaptor.forClass(ITouchpointAdvice.class); @Override public void setUp() throws Exception { - testAction = new FeaturesAction(new File[] {root}); - tpAdvice = Capture.newInstance(); + super.setUp(); + testAction = Mockito.spy(new FeaturesAction(new File[] { root })); setupPublisherInfo(); setupPublisherResult(); } @@ -90,37 +91,41 @@ public class FeaturesActionTest extends ActionTest { */ public void testSimple() throws Exception { testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor()); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(capture.capture()); verifyRepositoryContents(); debug("Completed FeaturesAction."); //$NON-NLS-1$ } public void testFeaturePatch() throws Exception { File testFolder = getTestFolder("FeaturesAction.testFilters"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<feature id=\"test.feature\" version=\"1.0.0\" > \n"); buffer.append(" <requires> \n"); - buffer.append(" <import feature=\"org.foo\" version=\"[1.0.0,2.0.0)\" match=\"versionRange\" patch=\"true\"/> \n"); + buffer.append( + " <import feature=\"org.foo\" version=\"[1.0.0,2.0.0)\" match=\"versionRange\" patch=\"true\"/> \n"); buffer.append(" </requires> \n"); buffer.append("</feature> \n"); File featureXML = new File(testFolder, "feature.xml"); writeBuffer(featureXML, buffer); publisherInfo = new PublisherInfo(); - FeaturesAction action = new FeaturesAction(new File[] {testFolder}); + FeaturesAction action = new FeaturesAction(new File[] { testFolder }); action.perform(publisherInfo, publisherResult, new NullProgressMonitor()); - IInstallableUnitPatch iu = (IInstallableUnitPatch) publisherResult.getIU("test.feature.feature.group", Version.parseVersion("1.0.0"), null); + IInstallableUnitPatch iu = (IInstallableUnitPatch) publisherResult.getIU("test.feature.feature.group", + Version.parseVersion("1.0.0"), null); IRequirement[][] applicabilityScope = iu.getApplicabilityScope(); assertEquals(1, applicabilityScope.length); IRequiredCapability require = (IRequiredCapability) applicabilityScope[0][0]; - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.foo.feature.group", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, + "org.foo.feature.group", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } public void testMatchRange() throws Exception { File testFolder = getTestFolder("FeaturesAction.testFilters"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<feature id=\"test.feature\" version=\"1.0.0\" > \n"); buffer.append(" <requires> \n"); buffer.append(" <import plugin=\"org.plug\" version=\"[1.0.0,2.0.0)\" match=\"versionRange\" /> \n"); @@ -131,7 +136,7 @@ public class FeaturesActionTest extends ActionTest { writeBuffer(featureXML, buffer); publisherInfo = new PublisherInfo(); - FeaturesAction action = new FeaturesAction(new File[] {testFolder}); + FeaturesAction action = new FeaturesAction(new File[] { testFolder }); action.perform(publisherInfo, publisherResult, new NullProgressMonitor()); IInstallableUnit iu = publisherResult.getIU("test.feature.feature.group", Version.parseVersion("1.0.0"), null); @@ -141,10 +146,12 @@ public class FeaturesActionTest extends ActionTest { String requireName = ((IRequiredCapability) require).getName(); if (requireName.equals("org.foo.feature.group")) { - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.foo.feature.group", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, + "org.foo.feature.group", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } else if (requireName.equals("org.plug")) { - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.plug", VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.plug", + VersionRange.create("[1.0.0, 2.0.0)"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } } @@ -152,7 +159,7 @@ public class FeaturesActionTest extends ActionTest { public void testMatchGreaterOrEqual() throws Exception { File testFolder = getTestFolder("FeaturesAction.testFilters"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<feature id=\"test.feature\" version=\"1.0.0\" > \n"); buffer.append(" <requires> \n"); buffer.append(" <import plugin=\"org.plug\" version=\"1.0.0\" match=\"greaterOrEqual\" /> \n"); @@ -163,7 +170,7 @@ public class FeaturesActionTest extends ActionTest { writeBuffer(featureXML, buffer); publisherInfo = new PublisherInfo(); - FeaturesAction action = new FeaturesAction(new File[] {testFolder}); + FeaturesAction action = new FeaturesAction(new File[] { testFolder }); action.perform(publisherInfo, publisherResult, new NullProgressMonitor()); IInstallableUnit iu = publisherResult.getIU("test.feature.feature.group", Version.parseVersion("1.0.0"), null); @@ -173,10 +180,12 @@ public class FeaturesActionTest extends ActionTest { String requireName = ((IRequiredCapability) require).getName(); if (requireName.equals("org.foo.feature.group")) { - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.foo.feature.group", VersionRange.create("1.0.0"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, + "org.foo.feature.group", VersionRange.create("1.0.0"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } else if (requireName.equals("org.plug")) { - IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.plug", VersionRange.create("1.0.0"), null, false, false, true); + IRequirement expected = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "org.plug", + VersionRange.create("1.0.0"), null, false, false, true); verifyRequirement(Collections.singleton(expected), require); } } @@ -184,7 +193,7 @@ public class FeaturesActionTest extends ActionTest { public void testFilters() throws Exception { File testFolder = getTestFolder("FeaturesAction.testFilters"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<feature id=\"test.feature\" version=\"1.0.0\" > \n"); buffer.append(" <includes id=\"org.foo\" version=\"1.0.0\" filter=\"(osgi.os=win32)\"/> \n"); buffer.append(" <plugin id=\"org.plug\" version=\"1.0.0\" filter=\"(my.prop=foo)\" os=\"win32\" /> \n"); @@ -197,7 +206,7 @@ public class FeaturesActionTest extends ActionTest { writeBuffer(featureXML, buffer); publisherInfo = new PublisherInfo(); - FeaturesAction action = new FeaturesAction(new File[] {testFolder}); + FeaturesAction action = new FeaturesAction(new File[] { testFolder }); action.perform(publisherInfo, publisherResult, new NullProgressMonitor()); IInstallableUnit iu = publisherResult.getIU("test.feature.feature.group", Version.parseVersion("1.0.0"), null); @@ -207,7 +216,8 @@ public class FeaturesActionTest extends ActionTest { if (((IRequiredCapability) require).getName().equals("org.foo.feature.group")) { assertEquals(ExpressionUtil.parseLDAP("(osgi.os=win32)"), require.getFilter().getParameters()[0]); } else if (((IRequiredCapability) require).getName().equals("org.plug")) { - assertEquals(ExpressionUtil.parseLDAP("(&(my.prop=foo)(osgi.os=win32))"), require.getFilter().getParameters()[0]); + assertEquals(ExpressionUtil.parseLDAP("(&(my.prop=foo)(osgi.os=win32))"), + require.getFilter().getParameters()[0]); } else if (((IRequiredCapability) require).getName().equals("org.plug2")) { assertEquals(ExpressionUtil.parseLDAP("(my.prop=foo)"), require.getFilter().getParameters()[0]); } else if (((IRequiredCapability) require).getName().equals("org.foo2.feature.group")) { @@ -222,151 +232,173 @@ public class FeaturesActionTest extends ActionTest { } private void verifyMetadata() { - //{foo.feature.jar=[foo.feature.jar 1.0.0], bar.feature.jar=[bar.feature.jar 1.1.1], foo.feature.group=[foo.feature.group 1.0.0], bar.feature.group=[bar.feature.group 1.1.1]} - ArrayList<IInstallableUnit> fooIUs = new ArrayList<>(publisherResult.getIUs("foo.feature.jar", IPublisherResult.NON_ROOT)); //$NON-NLS-1$ - assertTrue(fooIUs.size() == 1); + // {foo.feature.jar=[foo.feature.jar 1.0.0], bar.feature.jar=[bar.feature.jar + // 1.1.1], foo.feature.group=[foo.feature.group 1.0.0], + // bar.feature.group=[bar.feature.group 1.1.1]} + ArrayList<IInstallableUnit> fooIUs = new ArrayList<>( + publisherResult.getIUs("foo.feature.jar", IPublisherResult.NON_ROOT)); //$NON-NLS-1$ + assertEquals(1, fooIUs.size()); IInstallableUnit foo = fooIUs.get(0); assertTrue(foo.getId().equalsIgnoreCase("foo.feature.jar")); //$NON-NLS-1$ - assertTrue(foo.getVersion().equals(fooVersion)); + assertEquals(fooVersion, foo.getVersion()); assertEquals("Foo Feature", foo.getProperty(IInstallableUnit.PROP_NAME)); assertEquals("Foo Description", foo.getProperty(IInstallableUnit.PROP_DESCRIPTION)); assertEquals("Foo License", foo.getLicenses().iterator().next().getBody()); assertEquals("Foo Copyright", foo.getCopyright().getBody()); - assertTrue(foo.getProperty("key1").equals("value1")); //$NON-NLS-1$ //$NON-NLS-2$ - assertTrue(foo.getProperty("key2").equals("value2")); //$NON-NLS-1$//$NON-NLS-2$ - assertTrue(foo.getArtifacts().iterator().next().equals(FOO_KEY)); - assertEquals(foo.getFilter().getParameters()[0], ExpressionUtil.parseLDAP("(org.eclipse.update.install.features=true)")); //$NON-NLS-1$ + assertEquals("value1", foo.getProperty("key1")); //$NON-NLS-1$ //$NON-NLS-2$ + assertEquals("value2", foo.getProperty("key2")); //$NON-NLS-1$//$NON-NLS-2$ + assertEquals(FOO_KEY, foo.getArtifacts().iterator().next()); + assertEquals(foo.getFilter().getParameters()[0], + ExpressionUtil.parseLDAP("(org.eclipse.update.install.features=true)")); //$NON-NLS-1$ - //check touchpointType + // check touchpointType assertTrue(foo.getTouchpointType().getId().equalsIgnoreCase("org.eclipse.equinox.p2.osgi")); //$NON-NLS-1$ - assertTrue(foo.getTouchpointType().getVersion().equals(fooVersion)); + assertEquals(fooVersion, foo.getTouchpointType().getVersion()); - //zipped=true + // zipped=true Collection<ITouchpointData> tpData = foo.getTouchpointData(); String fooValue = tpData.iterator().next().getInstructions().get("zipped").getBody(); //$NON-NLS-1$ assertTrue(fooValue.equalsIgnoreCase("true")); //$NON-NLS-1$ Collection<IRequirement> fooRequiredCapabilities = foo.getRequirements(); - assertTrue(fooRequiredCapabilities.size() == 0); + assertTrue(fooRequiredCapabilities.isEmpty()); Collection<IProvidedCapability> fooProvidedCapabilities = foo.getProvidedCapabilities(); - verifyProvidedCapability(fooProvidedCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "foo.feature.jar", fooVersion); //$NON-NLS-1$ - verifyProvidedCapability(fooProvidedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "feature", fooVersion); //$NON-NLS-1$ + verifyProvidedCapability(fooProvidedCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "foo.feature.jar", //$NON-NLS-1$ + fooVersion); + verifyProvidedCapability(fooProvidedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "feature", //$NON-NLS-1$ + fooVersion); verifyProvidedCapability(fooProvidedCapabilities, "org.eclipse.update.feature", FOO, fooVersion); //$NON-NLS-1$ - assertTrue(fooProvidedCapabilities.size() == 3); + assertEquals(3, fooProvidedCapabilities.size()); - //feature group IU for foo + // feature group IU for foo fooIUs = new ArrayList<>(publisherResult.getIUs("foo.feature.group", IPublisherResult.ROOT)); //$NON-NLS-1$ - assertTrue(fooIUs.size() == 1); + assertEquals(1, fooIUs.size()); IInstallableUnit fooGroup = fooIUs.get(0); - tpData = fooGroup.getTouchpointData(); - assertEquals(1, tpData.size()); - ITouchpointInstruction instruction = tpData.iterator().next().getInstruction("install"); + ITouchpointAdvice tData = capture.getValue(); + ITouchpointInstruction instruction = tData.getTouchpointData(NO_TP_DATA).getInstruction("install"); assertNotNull(instruction); - assertEquals("ln(targetDir:@artifact,linkTarget:foo/lib.1.so,linkName:lib.so);chmod(targetDir:@artifact,targetFile:lib/lib.so,permissions:755);", instruction.getBody()); + assertEquals( + "ln(targetDir:@artifact,linkTarget:foo/lib.1.so,linkName:lib.so);chmod(targetDir:@artifact,targetFile:lib/lib.so,permissions:755);", + instruction.getBody()); assertNull(fooGroup.getFilter()); - /*verify bar*/ - ArrayList<IInstallableUnit> barIUs = new ArrayList<>(publisherResult.getIUs("bar.feature.jar", IPublisherResult.NON_ROOT)); //$NON-NLS-1$ - assertTrue(barIUs.size() == 1); + /* verify bar */ + ArrayList<IInstallableUnit> barIUs = new ArrayList<>( + publisherResult.getIUs("bar.feature.jar", IPublisherResult.NON_ROOT)); //$NON-NLS-1$ + assertEquals(1, barIUs.size()); IInstallableUnit bar = barIUs.get(0); - assertTrue(bar.getId().equals("bar.feature.jar")); //$NON-NLS-1$ - assertTrue(bar.getVersion().equals(barVersion)); - assertTrue(bar.getProperty("key1").equals("value1")); //$NON-NLS-1$//$NON-NLS-2$ - assertTrue(bar.getProperty("key2").equals("value2")); //$NON-NLS-1$//$NON-NLS-2$ + assertEquals("bar.feature.jar", bar.getId()); //$NON-NLS-1$ + assertEquals(barVersion, bar.getVersion()); + assertEquals("value1", bar.getProperty("key1")); //$NON-NLS-1$//$NON-NLS-2$ + assertEquals("value2", bar.getProperty("key2")); //$NON-NLS-1$//$NON-NLS-2$ assertTrue(bar.getProperties().containsKey("org.eclipse.update.installHandler")); //$NON-NLS-1$ assertTrue(bar.getProperties().containsValue("handler=bar handler")); //$NON-NLS-1$ - assertTrue(bar.getArtifacts().iterator().next().equals(BAR_KEY)); - assertEquals(bar.getFilter().getParameters()[0], ExpressionUtil.parseLDAP("(org.eclipse.update.install.features=true)")); //$NON-NLS-1$ + assertEquals(BAR_KEY, bar.getArtifacts().iterator().next()); + assertEquals(bar.getFilter().getParameters()[0], + ExpressionUtil.parseLDAP("(org.eclipse.update.install.features=true)")); //$NON-NLS-1$ assertTrue(bar.isSingleton()); barIUs = new ArrayList<>(publisherResult.getIUs("bar.feature.group", IPublisherResult.ROOT)); //$NON-NLS-1$ - assertTrue(fooIUs.size() == 1); + assertEquals(1, fooIUs.size()); IInstallableUnit barGroup = barIUs.get(0); Collection<IRequirement> barRequiredCapabilities = barGroup.getRequirements(); - //contains(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar_root", new VersionRange(barVersion, true, barVersion, true), null, false /*multiple*/, false /*optional*/); //$NON-NLS-1$//$NON-NLS-2$ - verifyRequirement(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar.feature.jar", new VersionRange(barVersion, true, barVersion, true), "(org.eclipse.update.install.features=true)", 1, 1, true); //$NON-NLS-1$//$NON-NLS-2$ - verifyRequirement(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "org.bar.feature.feature.group", VersionRange.emptyRange, "(&(|(osgi.nl=de)(osgi.nl=en)(osgi.nl=fr)))", 1, 1, true); //$NON-NLS-1$//$NON-NLS-2$ - assertEquals(barGroup.getFilter().getParameters()[0], ExpressionUtil.parseLDAP("(&(|(osgi.os=macosx)(osgi.os=win32))(|(osgi.ws=carbon)(osgi.ws=win32))(|(osgi.arch=ppc)(osgi.arch=x86))(osgi.nl=en))")); - - //check zipped=true in touchpointData + // contains(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, + // "bar_root", new VersionRange(barVersion, true, barVersion, true), null, false + // /*multiple*/, false /*optional*/); //$NON-NLS-1$//$NON-NLS-2$ + verifyRequirement(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar.feature.jar", //$NON-NLS-1$ + new VersionRange(barVersion, true, barVersion, true), "(org.eclipse.update.install.features=true)", 1, //$NON-NLS-1$ + 1, true); + verifyRequirement(barRequiredCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "org.bar.feature.feature.group", //$NON-NLS-1$ + VersionRange.emptyRange, "(&(|(osgi.nl=de)(osgi.nl=en)(osgi.nl=fr)))", 1, 1, true); //$NON-NLS-1$ + assertEquals(barGroup.getFilter().getParameters()[0], ExpressionUtil.parseLDAP( + "(&(|(osgi.os=macosx)(osgi.os=win32))(|(osgi.ws=carbon)(osgi.ws=win32))(|(osgi.arch=ppc)(osgi.arch=x86))(osgi.nl=en))")); + + // check zipped=true in touchpointData String barValue = bar.getTouchpointData().iterator().next().getInstructions().get("zipped").getBody(); //$NON-NLS-1$ assertTrue(barValue.equalsIgnoreCase("true")); //$NON-NLS-1$ - //check touchpointType + // check touchpointType assertTrue(bar.getTouchpointType().getId().equalsIgnoreCase("org.eclipse.equinox.p2.osgi")); //$NON-NLS-1$ - assertTrue(bar.getTouchpointType().getVersion().equals(fooVersion)); - //String namespace, String name, VersionRange range, String filter, boolean optional, boolean multiple, boolean greedy) + assertEquals(fooVersion, bar.getTouchpointType().getVersion()); + // String namespace, String name, VersionRange range, String filter, boolean + // optional, boolean multiple, boolean greedy) barRequiredCapabilities = bar.getRequirements(); - assertTrue(barRequiredCapabilities.size() == 0); + assertTrue(barRequiredCapabilities.isEmpty()); Collection<IProvidedCapability> barProvidedCapabilities = bar.getProvidedCapabilities(); - verifyProvidedCapability(barProvidedCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar.feature.jar", barVersion); //$NON-NLS-1$ - verifyProvidedCapability(barProvidedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "feature", fooVersion); //$NON-NLS-1$ + verifyProvidedCapability(barProvidedCapabilities, IInstallableUnit.NAMESPACE_IU_ID, "bar.feature.jar", //$NON-NLS-1$ + barVersion); + verifyProvidedCapability(barProvidedCapabilities, PublisherHelper.NAMESPACE_ECLIPSE_TYPE, "feature", //$NON-NLS-1$ + fooVersion); verifyProvidedCapability(barProvidedCapabilities, "org.eclipse.update.feature", BAR, barVersion); //$NON-NLS-1$ - assertTrue(barProvidedCapabilities.size() == 3); + assertEquals(3, barProvidedCapabilities.size()); } private void verifyArtifacts() throws IOException { ZipInputStream actualStream = artifactRepository.getZipInputStream(FOO_KEY); - Map<String, Object[]> expected = getFileMap(new HashMap<>(), new File[] {new File(root, FOO)}, new Path(new File(root, FOO).getAbsolutePath())); + Map<String, Object[]> expected = getFileMap(new HashMap<>(), new File[] { new File(root, FOO) }, + new Path(new File(root, FOO).getAbsolutePath())); TestData.assertContains(expected, actualStream, true); - expected = getFileMap(new HashMap<>(), new File[] {new File(root, BAR)}, new Path(new File(root, BAR).getAbsolutePath())); + expected = getFileMap(new HashMap<>(), new File[] { new File(root, BAR) }, + new Path(new File(root, BAR).getAbsolutePath())); actualStream = artifactRepository.getZipInputStream(BAR_KEY); TestData.assertContains(expected, actualStream, true); } @Override protected void insertPublisherInfoBehavior() { - //setup metadataRepository with barIU - metadataRepository = new TestMetadataRepository(getAgent(), new IInstallableUnit[] {mockIU(BAR, null)}); - - ArrayList<IPropertyAdvice> adviceCollection = fillAdvice(new ArrayList<>()); - expect(publisherInfo.getAdvice(null, false, "bar.feature.jar", barVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar", barVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar", barVersion, IFeatureRootAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, ITouchpointAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, ICapabilityAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, - IAdditionalInstallableUnitAdvice.class)).andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.jar", fooVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, IUpdateDescriptorAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo", fooVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo", fooVersion, IFeatureRootAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, IPropertyAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, ICapabilityAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, - IAdditionalInstallableUnitAdvice.class)).andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, IUpdateDescriptorAdvice.class)) - .andReturn(Collections.emptyList()).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getMetadataRepository()).andReturn(metadataRepository).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(null).anyTimes(); - - //capture any touchpoint advice, and return the captured advice when the action asks for it - publisherInfo.addAdvice(and(isA(ITouchpointAdvice.class), capture(tpAdvice))); - EasyMock.expectLastCall().anyTimes(); - expect(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, ITouchpointAdvice.class)).andReturn(new CaptureList<>(tpAdvice)).anyTimes(); + // setup metadataRepository with barIU + metadataRepository = new TestMetadataRepository(getAgent(), new IInstallableUnit[] { mockIU(BAR, null) }); + + List<IPropertyAdvice> adviceCollection = fillAdvice(new ArrayList<>()); + when(publisherInfo.getAdvice(null, false, "bar.feature.jar", barVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "bar", barVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "bar", barVersion, IFeatureRootAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, ITouchpointAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, ICapabilityAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, + IAdditionalInstallableUnitAdvice.class)).thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo.feature.jar", fooVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "bar.feature.group", barVersion, IUpdateDescriptorAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo", fooVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "foo", fooVersion, IFeatureRootAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, IPropertyAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, ICapabilityAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, + IAdditionalInstallableUnitAdvice.class)).thenReturn(Collections.emptyList()); + when(publisherInfo.getAdvice(null, false, "foo.feature.group", fooVersion, IUpdateDescriptorAdvice.class)) + .thenReturn(Collections.emptyList()); + when(publisherInfo.getArtifactOptions()) + .thenReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getMetadataRepository()).thenReturn(metadataRepository); + when(publisherInfo.getContextMetadataRepository()).thenReturn(null); } - private ArrayList<IPropertyAdvice> fillAdvice(ArrayList<IPropertyAdvice> adviceCollection) { + private List<IPropertyAdvice> fillAdvice(ArrayList<IPropertyAdvice> adviceCollection) { Map<String, String> prop = new HashMap<>(); prop.put("key1", "value1"); //$NON-NLS-1$//$NON-NLS-2$ prop.put("key2", "value2"); //$NON-NLS-1$//$NON-NLS-2$ - IPropertyAdvice propertyAdvice = EasyMock.createMock(IPropertyAdvice.class); - expect(propertyAdvice.getInstallableUnitProperties((InstallableUnitDescription) EasyMock.anyObject())).andReturn(prop).anyTimes(); - expect(propertyAdvice.getArtifactProperties((IInstallableUnit) EasyMock.anyObject(), (IArtifactDescriptor) EasyMock.anyObject())).andReturn(null).anyTimes(); - EasyMock.replay(propertyAdvice); + IPropertyAdvice propertyAdvice = mock(IPropertyAdvice.class); + when(propertyAdvice.getInstallableUnitProperties(any(InstallableUnitDescription.class))).thenReturn(prop); + when(propertyAdvice.getArtifactProperties(any(IInstallableUnit.class), any(IArtifactDescriptor.class))) + .thenReturn(null); adviceCollection.add(propertyAdvice); return adviceCollection; } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/JREActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/JREActionTest.java index d1d3a4a20..dc5719e14 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/JREActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/JREActionTest.java @@ -16,7 +16,6 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.errorStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.okStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.statusWithMessageWhich; @@ -27,6 +26,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; +import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.io.File; @@ -276,7 +276,7 @@ public class JREActionTest extends ActionTest { @Override protected void insertPublisherInfoBehavior() { - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_PUBLISH).anyTimes(); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()).thenReturn(IPublisherInfo.A_PUBLISH); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/LocalUpdateSiteActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/LocalUpdateSiteActionTest.java index 706760c4e..53d5f03b8 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/LocalUpdateSiteActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/LocalUpdateSiteActionTest.java @@ -14,9 +14,11 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.anyBoolean; -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.expect; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; import java.io.File; import java.util.Collection; @@ -29,7 +31,6 @@ import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.ITouchpointData; import org.eclipse.equinox.p2.metadata.ITouchpointInstruction; import org.eclipse.equinox.p2.metadata.Version; -import org.eclipse.equinox.p2.publisher.IPublisherAdvice; import org.eclipse.equinox.p2.publisher.IPublisherInfo; import org.eclipse.equinox.p2.tests.TestData; import org.eclipse.equinox.p2.tests.publisher.TestArtifactRepository; @@ -48,10 +49,11 @@ public class LocalUpdateSiteActionTest extends ActionTest { @Override protected void insertPublisherInfoBehavior() { super.insertPublisherInfoBehavior(); - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getAdvice((String) anyObject(), anyBoolean(), (String) anyObject(), (Version) anyObject(), - (Class<IPublisherAdvice>) anyObject())).andReturn(Collections.emptyList()).anyTimes(); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()) + .thenReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH); + when(publisherInfo.getAdvice(anyString(), anyBoolean(), anyString(), any(Version.class), any(Class.class))) + .thenReturn(Collections.emptyList()); } /** diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionCapturingTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionCapturingTest.java index eb9a2d1b8..898c5f0fb 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionCapturingTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionCapturingTest.java @@ -16,65 +16,35 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.anyBoolean; -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.createNiceMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.expectLastCall; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.okStatus; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.verify; import java.io.File; -import java.util.Collections; -import org.easymock.Capture; -import org.easymock.EasyMock; +import java.util.stream.Collectors; import org.eclipse.core.runtime.IStatus; import org.eclipse.equinox.frameworkadmin.BundleInfo; import org.eclipse.equinox.internal.p2.publisher.eclipse.ProductFile; -import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.publisher.IPublisherAdvice; -import org.eclipse.equinox.p2.publisher.IPublisherInfo; -import org.eclipse.equinox.p2.publisher.actions.RootIUAdvice; import org.eclipse.equinox.p2.publisher.eclipse.IConfigAdvice; import org.eclipse.equinox.p2.publisher.eclipse.IExecutableAdvice; import org.eclipse.equinox.p2.publisher.eclipse.ProductAction; import org.eclipse.equinox.p2.publisher.eclipse.ProductFileAdvice; import org.eclipse.equinox.p2.tests.TestData; import org.eclipse.equinox.p2.tests.publisher.TestArtifactRepository; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; public class ProductActionCapturingTest extends ActionTest { File executablesFeatureLocation = null; String source = ""; - private Capture<RootIUAdvice> rootIUAdviceCapture; - private Capture<ProductFileAdvice> productFileAdviceCapture; protected TestArtifactRepository artifactRepository = new TestArtifactRepository(getAgent()); @Override - protected IPublisherInfo createPublisherInfoMock() { - //override to create a nice mock, because we don't care about other method calls. - return createNiceMock(IPublisherInfo.class); - } - - @Override - protected void insertPublisherInfoBehavior() { - // capture these calls for assertions - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(RootIUAdvice.class), EasyMock.capture(rootIUAdviceCapture))); - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(ProductFileAdvice.class), EasyMock.capture(productFileAdviceCapture))); - - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_PUBLISH).anyTimes(); - //Return an empty list every time getAdvice is called - expect(publisherInfo.getAdvice((String) anyObject(), anyBoolean(), (String) anyObject(), (Version) anyObject(), (Class<IPublisherAdvice>) anyObject())).andReturn(Collections.emptyList()); - expectLastCall().anyTimes(); - } - - @Override public void setUp() throws Exception { - rootIUAdviceCapture = Capture.newInstance(); - productFileAdviceCapture = Capture.newInstance(); setupPublisherInfo(); setupPublisherResult(); } @@ -84,15 +54,18 @@ public class ProductActionCapturingTest extends ActionTest { * IConfigAdvice (start levels, auto-start). */ public void testSetBundleConfigData() throws Exception { + ArgumentCaptor<IPublisherAdvice> productFileAdviceCapture = ArgumentCaptor.forClass(IPublisherAdvice.class); addContextIU("org.eclipse.rcp.feature.group", "3.5.0.v20081110-9C9tEvNEla71LZ2jFz-RFB-t"); ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "startLevel.product").toString()); - testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); + testAction = Mockito.spy(new ProductAction(source, productFile, flavorArg, executablesFeatureLocation)); IStatus status = testAction.perform(publisherInfo, publisherResult, null); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(productFileAdviceCapture.capture()); assertThat(status, is(okStatus())); - IConfigAdvice configAdvice = productFileAdviceCapture.getValue(); + IConfigAdvice configAdvice = (IConfigAdvice) productFileAdviceCapture.getAllValues().stream() + .filter(IConfigAdvice.class::isInstance).collect(Collectors.toList()).get(0); BundleInfo[] bundles = configAdvice.getBundles(); assertEquals("1.0", 2, bundles.length); assertEquals("1.1", "org.eclipse.equinox.common", bundles[0].getSymbolicName()); @@ -110,14 +83,17 @@ public class ProductActionCapturingTest extends ActionTest { * Tests that correct advice is created for the org.eclipse.platform product. */ public void testPlatformProduct() throws Exception { + ArgumentCaptor<IPublisherAdvice> productFileAdviceCapture = ArgumentCaptor.forClass(IPublisherAdvice.class); ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "platform.product").toString()); addContextIU("org.eclipse.platform.feature.group", "1.2.3"); - testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); + testAction = Mockito.spy(new ProductAction(source, productFile, flavorArg, executablesFeatureLocation)); IStatus status = testAction.perform(publisherInfo, publisherResult, null); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(productFileAdviceCapture.capture()); assertThat(status, is(okStatus())); - IExecutableAdvice launchAdvice = productFileAdviceCapture.getValue(); + IExecutableAdvice launchAdvice = (IExecutableAdvice) productFileAdviceCapture.getAllValues().stream() + .filter(ProductFileAdvice.class::isInstance).collect(Collectors.toList()).get(0); assertEquals("1.0", "eclipse", launchAdvice.getExecutableName()); String[] programArgs = launchAdvice.getProgramArguments(); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java index 3405c53e5..994cfafcd 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTest.java @@ -16,7 +16,6 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.createNiceMock; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.errorStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.okStatus; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.statusWithMessageWhich; @@ -71,21 +70,18 @@ public class ProductActionTest extends ActionTest { String source = ""; protected TestArtifactRepository artifactRepository = new TestArtifactRepository(getAgent()); - @Override protected IPublisherInfo createPublisherInfoMock() { - //override to create a nice mock, because we don't care about other method calls. - return createNiceMock(IPublisherInfo.class); - } - - @Override public void setUp() throws Exception { + @Override + public void setUp() throws Exception { setupPublisherInfo(); setupPublisherResult(); } - @Override public void setupPublisherInfo() { + @Override + public void setupPublisherInfo() { PublisherInfo publisherInfoImpl = new PublisherInfo(); publisherInfoImpl.setArtifactRepository(artifactRepository); publisherInfoImpl.setArtifactOptions(IPublisherInfo.A_PUBLISH); - publisherInfoImpl.setConfigurations(new String[] {configSpec}); + publisherInfoImpl.setConfigurations(new String[] { configSpec }); publisherInfo = publisherInfoImpl; } @@ -95,18 +91,20 @@ public class ProductActionTest extends ActionTest { * splash screen, icon, etc. */ public void testBrandedApplication() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()); addContextIU("org.eclipse.platform.feature.group", "1.2.3"); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("branded.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); - //TODO assert branding was done correctly + // TODO assert branding was done correctly } public void testLicense() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "productWithLicense.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "productWithLicense.product").toString()); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("licenseIU.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); @@ -116,7 +114,8 @@ public class ProductActionTest extends ActionTest { } public void testLicenseNoURL() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "licenseNoURL.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "licenseNoURL.product").toString()); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("licenseIU.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); @@ -126,7 +125,8 @@ public class ProductActionTest extends ActionTest { } public void testLicenseNoText() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "licenseNoText.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "licenseNoText.product").toString()); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("licenseIU.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); @@ -136,7 +136,8 @@ public class ProductActionTest extends ActionTest { } public void testMissingLicense() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "productWithNoLicense.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "productWithNoLicense.product").toString()); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("licenseIU.product", IPublisherResult.NON_ROOT); assertEquals("1.0", 1, ius.size()); @@ -145,8 +146,10 @@ public class ProductActionTest extends ActionTest { } public void testMultiProductPublishing() throws Exception { - ProductFile productFile1 = new ProductFile(TestData.getFile("ProductActionTest", "boundedVersionConfigurations.product").toString()); - ProductFile productFile2 = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile1 = new ProductFile( + TestData.getFile("ProductActionTest", "boundedVersionConfigurations.product").toString()); + ProductFile productFile2 = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); addContextIU("org.eclipse.core.runtime", "4.0.0"); performProductAction(productFile1); @@ -158,7 +161,8 @@ public class ProductActionTest extends ActionTest { } public void testMultiPlatformCUs_DifferentPlatforms() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); addContextIU("org.eclipse.core.runtime", "0.0.0", WIN_FILTER); @@ -169,7 +173,8 @@ public class ProductActionTest extends ActionTest { } public void testMultiPlatformCUs_SamePlatforms() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); addContextIU("org.eclipse.core.runtime", "0.0.0", LINUX_FILTER); @@ -180,7 +185,8 @@ public class ProductActionTest extends ActionTest { } public void testMultiPlatformCUs_SamePlatforms_NoVersion() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); addContextIU("org.eclipse.core.runtime", null, LINUX_FILTER); @@ -191,11 +197,13 @@ public class ProductActionTest extends ActionTest { } public void testMultiPlatformCUs_SamePlatforms_BoundedVersions() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); - // Set a specific version number, the one in the .product file uses 0.0.0. Let's see if it binds properly - //filter is different from linuxConfigSpec, but will still match + // Set a specific version number, the one in the .product file uses 0.0.0. Let's + // see if it binds properly + // filter is different from linuxConfigSpec, but will still match addContextIU("org.eclipse.core.runtime", "4.0.0", "(osgi.os=linux)"); performProductAction(productFile); @@ -205,36 +213,45 @@ public class ProductActionTest extends ActionTest { } public void testCUsHost() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); setConfiguration(LINUX_CONFIG_SPEC); - // Set a specific version number, the one in the .product file uses 0.0.0. Let's see if it binds properly - //filter is different from linuxConfigSpec, but will still match + // Set a specific version number, the one in the .product file uses 0.0.0. Let's + // see if it binds properly + // filter is different from linuxConfigSpec, but will still match addContextIU("org.eclipse.core.runtime", "4.0.0", "(osgi.os=linux)"); performProductAction(productFile); - IInstallableUnitFragment fragment = (IInstallableUnitFragment) getUniquePublishedIU(flavorArg + LINUX_CONFIG_SPEC + "org.eclipse.core.runtime"); - assertEquals("1.1", "org.eclipse.core.runtime", RequiredCapability.extractName(fragment.getHost().iterator().next().getMatches())); - assertEquals("1.2", Version.create("4.0.0"), RequiredCapability.extractRange(fragment.getHost().iterator().next().getMatches()).getMinimum()); + IInstallableUnitFragment fragment = (IInstallableUnitFragment) getUniquePublishedIU( + flavorArg + LINUX_CONFIG_SPEC + "org.eclipse.core.runtime"); + assertEquals("1.1", "org.eclipse.core.runtime", + RequiredCapability.extractName(fragment.getHost().iterator().next().getMatches())); + assertEquals("1.2", Version.create("4.0.0"), + RequiredCapability.extractRange(fragment.getHost().iterator().next().getMatches()).getMinimum()); assertEquals("1.3", Version.create("1.0.0"), fragment.getVersion()); } public void testMultiConfigspecProductPublishing() throws IOException, Exception { ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "platform.product").toString()); - ((PublisherInfo) publisherInfo).setConfigurations(new String[] {"carbon.macos.x86", "cocoa.macos.x86"}); + ((PublisherInfo) publisherInfo).setConfigurations(new String[] { "carbon.macos.x86", "cocoa.macos.x86" }); addContextIU("org.eclipse.platform.feature.group", "1.2.3"); performProductAction(productFile); - Collection<IConfigAdvice> advice = publisherInfo.getAdvice("carbon.macos.x86", false, null, null, IConfigAdvice.class); + Collection<IConfigAdvice> advice = publisherInfo.getAdvice("carbon.macos.x86", false, null, null, + IConfigAdvice.class); assertEquals("1.0", 1, advice.size()); } public void testANYConfigSpecPublishing_GeneralBundle() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); - String configSpecANY = AbstractPublisherAction.createConfigSpec("ANY", "ANY", "ANY"); // configuration spec to create CUs without filters + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + String configSpecANY = AbstractPublisherAction.createConfigSpec("ANY", "ANY", "ANY"); // configuration spec to + // create CUs without + // filters setConfiguration(configSpecANY); addContextIU("org.eclipse.core.runtime", "4.0.0"); @@ -242,16 +259,22 @@ public class ProductActionTest extends ActionTest { performProductAction(productFile); // there is a CU for the IU because it applies to all platforms - IInstallableUnitFragment fragment = (IInstallableUnitFragment) getUniquePublishedIU(flavorArg + configSpecANY + "org.eclipse.core.runtime"); - assertEquals("1.1", "org.eclipse.core.runtime", RequiredCapability.extractName(fragment.getHost().iterator().next().getMatches())); - assertEquals("1.2", Version.create("4.0.0"), RequiredCapability.extractRange(fragment.getHost().iterator().next().getMatches()).getMinimum()); + IInstallableUnitFragment fragment = (IInstallableUnitFragment) getUniquePublishedIU( + flavorArg + configSpecANY + "org.eclipse.core.runtime"); + assertEquals("1.1", "org.eclipse.core.runtime", + RequiredCapability.extractName(fragment.getHost().iterator().next().getMatches())); + assertEquals("1.2", Version.create("4.0.0"), + RequiredCapability.extractRange(fragment.getHost().iterator().next().getMatches()).getMinimum()); assertEquals("1.3", Version.create("1.0.0"), fragment.getVersion()); assertNull("1.3", fragment.getFilter()); } public void testANYConfigSpecPublishing_PlatformSpecificBundle() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); - String configSpecANY = AbstractPublisherAction.createConfigSpec("ANY", "ANY", "ANY"); // configuration spec to create CUs without filters + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "unboundedVersionConfigurations.product").toString()); + String configSpecANY = AbstractPublisherAction.createConfigSpec("ANY", "ANY", "ANY"); // configuration spec to + // create CUs without + // filters setConfiguration(configSpecANY); addContextIU("org.eclipse.core.runtime", "4.0.0", WIN_FILTER); // any valid filter can be set here @@ -276,13 +299,15 @@ public class ProductActionTest extends ActionTest { testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); PublisherInfo info = new PublisherInfo(); info.setContextMetadataRepository(repository); - // TODO this line doesn't have any effect -> is this a bug in the implementation? + // TODO this line doesn't have any effect -> is this a bug in the + // implementation? info.addAdvice(new QueryableFilterAdvice(info.getContextMetadataRepository())); IStatus status = testAction.perform(info, publisherResult, null); assertThat(status, is(okStatus())); - IQueryResult<IInstallableUnit> results = publisherResult.query(QueryUtil.createIUQuery("org.eclipse.platform.ide", Version.create("3.5.0.I20081118")), null); + IQueryResult<IInstallableUnit> results = publisherResult + .query(QueryUtil.createIUQuery("org.eclipse.platform.ide", Version.create("3.5.0.I20081118")), null); assertEquals("1.0", 1, queryResultSize(results)); IInstallableUnit unit = results.iterator().next(); Collection<IRequirement> requiredCapabilities = unit.getRequirements(); @@ -296,12 +321,14 @@ public class ProductActionTest extends ActionTest { } } assertTrue("1.1", capability != null); - assertEquals("1.2", InstallableUnit.parseFilter("(org.eclipse.update.install.features=true)"), capability.getFilter()); + assertEquals("1.2", InstallableUnit.parseFilter("(org.eclipse.update.install.features=true)"), + capability.getFilter()); } public void testProductWithAdviceFile() throws Exception { // product file that has a corresponding advice file (p2.inf). - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest/productWithAdvice", "productWithAdvice.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest/productWithAdvice", "productWithAdvice.product").toString()); testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); IStatus status = testAction.perform(new PublisherInfo(), publisherResult, null); assertThat(status, is(okStatus())); @@ -310,27 +337,30 @@ public class ProductActionTest extends ActionTest { Collection<ITouchpointData> data = product.getTouchpointData(); assertEquals("1.1", 1, data.size()); String configure = data.iterator().next().getInstruction("configure").getBody(); - assertEquals("1.2", "addRepository(type:0,location:http${#58}//download.eclipse.org/releases/fred);addRepository(type:1,location:http${#58}//download.eclipse.org/releases/fred);", configure); - - //update.id = com.zoobar - //update.range = [4.0,4.3) - //update.severity = 0 - //update.description = This is the description + assertEquals("1.2", + "addRepository(type:0,location:http${#58}//download.eclipse.org/releases/fred);addRepository(type:1,location:http${#58}//download.eclipse.org/releases/fred);", + configure); + + // update.id = com.zoobar + // update.range = [4.0,4.3) + // update.severity = 0 + // update.description = This is the description IUpdateDescriptor update = product.getUpdateDescriptor(); assertEquals("2.0", 0, update.getSeverity()); assertEquals("2.1", "This is the description", update.getDescription()); - //unit that fits in range + // unit that fits in range assertTrue("2.2", update.isUpdateOf(createIU("com.zoobar", Version.createOSGi(4, 1, 0)))); - //unit that is too old for range + // unit that is too old for range assertFalse("2.3", update.isUpdateOf(createIU("com.zoobar", Version.createOSGi(3, 1, 0)))); - //version that is too new and outside of range + // version that is too new and outside of range assertFalse("2.4", update.isUpdateOf(createIU("com.zoobar", Version.createOSGi(6, 1, 0)))); - //unit with matching version but not matching id + // unit with matching version but not matching id assertFalse("2.6", update.isUpdateOf(createIU("com.other", Version.createOSGi(4, 1, 0)))); } public void testFiltersOfInclusions() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "productIncludingFragments.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "productIncludingFragments.product").toString()); addContextIU("generalbundle", "1.0.1"); addContextIU("fragment.win", "1.0.2", WIN_FILTER); // no fragment.linux in the context @@ -338,39 +368,48 @@ public class ProductActionTest extends ActionTest { IStatus status = performProductActionAndReturnStatus(productFile); IInstallableUnit productIU = getUniquePublishedIU("productIncludingFragments.uid"); - assertThat(productIU.getRequirements(), hasItem(createIURequirement("generalbundle", createStrictVersionRange("1.0.1")))); - assertThat(productIU.getRequirements(), hasItem(createIURequirement("fragment.win", createStrictVersionRange("1.0.2"), WIN_FILTER))); + assertThat(productIU.getRequirements(), + hasItem(createIURequirement("generalbundle", createStrictVersionRange("1.0.1")))); + assertThat(productIU.getRequirements(), + hasItem(createIURequirement("fragment.win", createStrictVersionRange("1.0.2"), WIN_FILTER))); - // this is bug 390361: the Linux fragment is required without filter, so the product cannot be installed for Windows ... + // this is bug 390361: the Linux fragment is required without filter, so the + // product cannot be installed for Windows ... assertThat(productIU.getRequirements(), hasItem(createIURequirement("fragment.linux", ANY_VERSION))); // ... therefore the action shall report an error assertThat(status, is(errorStatus())); - assertThat(Arrays.asList(status.getChildren()), hasItem(statusWithMessageWhich(containsString("Included element fragment.linux 0.0.0 is missing")))); + assertThat(Arrays.asList(status.getChildren()), + hasItem(statusWithMessageWhich(containsString("Included element fragment.linux 0.0.0 is missing")))); } public void testMessageForProductWithIgnoredContent() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "mixedContentIgnored.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "mixedContentIgnored.product").toString()); IStatus status = performProductActionAndReturnStatus(productFile); - // expect a warning about redundant, ignored content in product file -> requested in bug 325611 + // expect a warning about redundant, ignored content in product file -> + // requested in bug 325611 assertThat(Arrays.asList(status.getChildren()), hasItem(statusWithMessageWhich(containsString("are ignored")))); // TODO the message should have a code identifying it } public void testJREIncluded() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "brandedProduct/branded.product").toString()); addContextIU("org.eclipse.platform.feature.group", "1.2.3"); performProductAction(productFile); Collection<IInstallableUnit> ius = publisherResult.getIUs("branded.product", IPublisherResult.NON_ROOT); assertEquals(1, ius.size()); assertEquals("Missing a.jre.javase", 1, publisherResult.getIUs("a.jre.javase", IPublisherResult.ROOT).size()); - assertEquals("Missing config.a.jre.javase", 1, publisherResult.getIUs("config.a.jre.javase", IPublisherResult.ROOT).size()); + assertEquals("Missing config.a.jre.javase", 1, + publisherResult.getIUs("config.a.jre.javase", IPublisherResult.ROOT).size()); } public void testRequiredEEAsSpecified() throws Exception { - ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "productFileActionTest.product").toString()); + ProductFile productFile = new ProductFile( + TestData.getFile("ProductActionTest", "productFileActionTest.product").toString()); addContextIU("org.eclipse.core.commands", "5.0.0"); performProductAction(productFile); @@ -407,7 +446,7 @@ public class ProductActionTest extends ActionTest { } private void setConfiguration(String configSpec) { - ((PublisherInfo) publisherInfo).setConfigurations(new String[] {configSpec}); + ((PublisherInfo) publisherInfo).setConfigurations(new String[] { configSpec }); } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTestMac.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTestMac.java index a14b660b5..7673a092c 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTestMac.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductActionTestMac.java @@ -13,30 +13,23 @@ *******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.anyBoolean; -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.createNiceMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.expectLastCall; import static org.eclipse.equinox.p2.tests.publisher.actions.StatusMatchers.okStatus; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.verify; import java.io.File; -import java.util.Collections; -import org.easymock.Capture; -import org.easymock.EasyMock; +import java.util.stream.Collectors; import org.eclipse.core.runtime.IStatus; import org.eclipse.equinox.internal.p2.publisher.eclipse.ProductFile; -import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.publisher.AbstractPublisherAction; import org.eclipse.equinox.p2.publisher.IPublisherAdvice; -import org.eclipse.equinox.p2.publisher.IPublisherInfo; -import org.eclipse.equinox.p2.publisher.actions.RootIUAdvice; import org.eclipse.equinox.p2.publisher.eclipse.IExecutableAdvice; import org.eclipse.equinox.p2.publisher.eclipse.ProductAction; import org.eclipse.equinox.p2.publisher.eclipse.ProductFileAdvice; import org.eclipse.equinox.p2.tests.TestData; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; /** * Tests for {@link ProductAction} specific to Mac. @@ -44,30 +37,11 @@ import org.eclipse.equinox.p2.tests.TestData; public class ProductActionTestMac extends ActionTest { private File executablesFeatureLocation = null; - private Capture<RootIUAdvice> rootIUAdviceCapture; - private Capture<ProductFileAdvice> productFileAdviceCapture; private String source = ""; @Override - protected IPublisherInfo createPublisherInfoMock() { - //override to create a nice mock, because we don't care about other method calls. - return createNiceMock(IPublisherInfo.class); - } - - @Override - protected void insertPublisherInfoBehavior() { - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(RootIUAdvice.class), EasyMock.capture(rootIUAdviceCapture))); - publisherInfo.addAdvice(EasyMock.and(EasyMock.isA(ProductFileAdvice.class), EasyMock.capture(productFileAdviceCapture))); - //Return an empty list every time getAdvice is called - expect(publisherInfo.getAdvice((String) anyObject(), anyBoolean(), (String) anyObject(), (Version) anyObject(), (Class<IPublisherAdvice>) anyObject())).andReturn(Collections.emptyList()); - expectLastCall().anyTimes(); - } - - @Override public void setUp() throws Exception { configSpec = AbstractPublisherAction.createConfigSpec("carbon", "macosx", "x86"); - rootIUAdviceCapture = Capture.newInstance(); - productFileAdviceCapture = Capture.newInstance(); setupPublisherInfo(); setupPublisherResult(); } @@ -76,13 +50,16 @@ public class ProductActionTestMac extends ActionTest { * Tests that correct advice is created for the org.eclipse.platform product. */ public void testPlatformProduct() throws Exception { + ArgumentCaptor<IPublisherAdvice> productFileAdviceCapture = ArgumentCaptor.forClass(IPublisherAdvice.class); ProductFile productFile = new ProductFile(TestData.getFile("ProductActionTest", "platform.product").toString()); addContextIU("org.eclipse.platform.feature.group", "3.8.3"); - testAction = new ProductAction(source, productFile, flavorArg, executablesFeatureLocation); + testAction = Mockito.spy(new ProductAction(source, productFile, flavorArg, executablesFeatureLocation)); IStatus status = testAction.perform(publisherInfo, publisherResult, null); + verify(publisherInfo, Mockito.atLeastOnce()).addAdvice(productFileAdviceCapture.capture()); assertThat(status, is(okStatus())); - IExecutableAdvice launchAdvice = productFileAdviceCapture.getValue(); + IExecutableAdvice launchAdvice = (IExecutableAdvice) productFileAdviceCapture.getAllValues().stream() + .filter(ProductFileAdvice.class::isInstance).collect(Collectors.toList()).get(0); assertEquals("1.0", "eclipse", launchAdvice.getExecutableName()); String[] programArgs = launchAdvice.getProgramArguments(); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductFileAdviceTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductFileAdviceTest.java index 3a93eb59a..91bc64d39 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductFileAdviceTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/ProductFileAdviceTest.java @@ -208,7 +208,7 @@ public class ProductFileAdviceTest extends AbstractProvisioningTest { configProperties.put("osgi.bundles", "org.eclipse.equinox.simpleconfigurator@1:start"); writeProperties(new File(rootFolder, "config.ini"), configProperties); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("org.eclipse.equinox.common,3.5.100.v20090817,plugins/org.eclipse.equinox.common_3.5.100.v20090817.jar,2,true\n"); buffer.append("org.eclipse.equinox.simpleconfigurator,1.0.200.v20090729-1800,plugins/org.eclipse.equinox.simpleconfigurator_1.0.200.v20090729-1800.jar,1,true\n"); writeBuffer(new File(rootFolder, "org.eclipse.equinox.simpleconfigurator/bundles.info"), buffer); @@ -229,7 +229,7 @@ public class ProductFileAdviceTest extends AbstractProvisioningTest { File root = getTestFolder("configNullLauncher"); File testProduct = new File(root, "test.product"); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("<product id=\"test.product\" version=\"1\" useFeatures=\"false\"> \n"); buffer.append(" <configIni use=\"default\"> \n"); buffer.append(" <win32>config.ini</win32> \n"); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootFilesActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootFilesActionTest.java index 9fa2a5d65..030cd8b2a 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootFilesActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootFilesActionTest.java @@ -15,19 +15,30 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; - -import java.io.*; -import java.util.*; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.zip.ZipInputStream; -import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; import org.eclipse.equinox.internal.p2.core.helpers.FileUtils; import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.publisher.AbstractPublisherAction; import org.eclipse.equinox.p2.publisher.IPublisherInfo; -import org.eclipse.equinox.p2.publisher.actions.*; +import org.eclipse.equinox.p2.publisher.actions.IRootFilesAdvice; +import org.eclipse.equinox.p2.publisher.actions.ITouchpointAdvice; +import org.eclipse.equinox.p2.publisher.actions.RootFilesAction; +import org.eclipse.equinox.p2.publisher.actions.RootFilesAdvice; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; import org.eclipse.equinox.p2.tests.TestActivator; import org.eclipse.equinox.p2.tests.TestData; @@ -68,10 +79,13 @@ public class RootFilesActionTest extends ActionTest { @Override public void insertPublisherInfoBehavior() { - expect(publisherInfo.getArtifactRepository()).andReturn(artifactRepository).anyTimes(); - expect(publisherInfo.getArtifactOptions()).andReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH).anyTimes(); - expect(publisherInfo.getAdvice(configSpec, true, null, null, IRootFilesAdvice.class)).andReturn(adviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(configSpec, false, flavorArg + topArg, versionArg, ITouchpointAdvice.class)).andReturn(null).anyTimes(); + when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); + when(publisherInfo.getArtifactOptions()) + .thenReturn(IPublisherInfo.A_INDEX | IPublisherInfo.A_OVERWRITE | IPublisherInfo.A_PUBLISH); + when(publisherInfo.getAdvice(configSpec, true, null, null, IRootFilesAdvice.class)) + .thenReturn(adviceCollection); + when(publisherInfo.getAdvice(configSpec, false, flavorArg + topArg, versionArg, ITouchpointAdvice.class)) + .thenReturn(null); } private void setupTestCase(int testArg) throws Exception { diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootIUActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootIUActionTest.java index 21b4dd4b2..637465c39 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootIUActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/actions/RootIUActionTest.java @@ -15,7 +15,7 @@ ******************************************************************************/ package org.eclipse.equinox.p2.tests.publisher.actions; -import static org.easymock.EasyMock.expect; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Collection; @@ -358,16 +358,22 @@ public class RootIUActionTest extends ActionTest { @Override public void insertPublisherInfoBehavior() { - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, ICapabilityAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, true, null, null, IRootIUAdvice.class)).andReturn(rootIUAdviceCollection).anyTimes(); - expect(publisherInfo.getAdvice(null, true, null, null, IVersionAdvice.class)).andReturn(null).anyTimes(); - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, ITouchpointAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, IUpdateDescriptorAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, IPropertyAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, false, rootIU, versionArg, IAdditionalInstallableUnitAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getAdvice(null, true, rootIU, versionArg, ILicenseAdvice.class)).andReturn(new ArrayList<>()).anyTimes(); - expect(publisherInfo.getMetadataRepository()).andReturn(metadataRepository).anyTimes(); - expect(publisherInfo.getContextMetadataRepository()).andReturn(null).anyTimes(); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, ICapabilityAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, true, null, null, IRootIUAdvice.class)).thenReturn(rootIUAdviceCollection); + when(publisherInfo.getAdvice(null, true, null, null, IVersionAdvice.class)).thenReturn(null); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, ITouchpointAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, IUpdateDescriptorAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, IPropertyAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, false, rootIU, versionArg, IAdditionalInstallableUnitAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getAdvice(null, true, rootIU, versionArg, ILicenseAdvice.class)) + .thenReturn(new ArrayList<>()); + when(publisherInfo.getMetadataRepository()).thenReturn(metadataRepository); + when(publisherInfo.getContextMetadataRepository()).thenReturn(null); } @Override diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/reconciler/dropins/AbstractReconcilerTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/reconciler/dropins/AbstractReconcilerTest.java index f1b090aa9..81d99b186 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/reconciler/dropins/AbstractReconcilerTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/reconciler/dropins/AbstractReconcilerTest.java @@ -281,7 +281,7 @@ public class AbstractReconcilerTest extends AbstractProvisioningTest { detailedMessage.append(" install location is ").append(getInstallLocation()).append('\n'); String message = "Need to set the \"org.eclipse.equinox.p2.reconciler.tests.platform.archive\" system property with a valid path to the platform binary drop or copy the archive to be a sibling of the install folder."; assertNotNull(message + "\n" + detailedMessage, file); - assertTrue(message, file.exists()); + assertTrue(message + "\nThe file '" + file.getAbsolutePath() + "' does not exist", file.exists()); assertTrue("File is zero length: " + file.getAbsolutePath(), file.length() > 0); return file; } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/touchpoint/eclipse/SetLauncherNameActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/touchpoint/eclipse/SetLauncherNameActionTest.java index de6ddd662..f01f88db3 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/touchpoint/eclipse/SetLauncherNameActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/touchpoint/eclipse/SetLauncherNameActionTest.java @@ -14,7 +14,9 @@ package org.eclipse.equinox.p2.tests.touchpoint.eclipse; import java.io.File; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.eclipse.equinox.frameworkadmin.BundleInfo; import org.eclipse.equinox.internal.p2.engine.InstallableUnitOperand; import org.eclipse.equinox.internal.p2.touchpoint.eclipse.EclipseTouchpoint; @@ -99,7 +101,7 @@ public class SetLauncherNameActionTest extends AbstractProvisioningTest { //profile will start using "eclipse" by default, give it some content and see if it //survives a name change. File eclipseIni = new File(tempFolder, "eclipse.ini"); - StringBuffer ini = new StringBuffer(); + StringBuilder ini = new StringBuilder(); ini.append("-startup\n"); ini.append("plugins/org.eclipse.equinox.launcher_1.2.4.v1234.jar\n"); writeBuffer(eclipseIni, ini); diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/generator/Europa/site.xml b/bundles/org.eclipse.equinox.p2.tests/testData/generator/Europa/site.xml index a9bf0170d..d9102ed21 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/generator/Europa/site.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/generator/Europa/site.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<site digestURL="http://download.eclipse.org/releases/europa/" mirrorsURL="http://www.eclipse.org/downloads/download.php?file=/releases/europa/site.xml&format=xml&protocol=http" pack200="true"> +<site digestURL="https://download.eclipse.org/releases/europa/" mirrorsURL="http://www.eclipse.org/downloads/download.php?file=/releases/europa/site.xml&format=xml&protocol=http" pack200="true"> <description url="http://www.eclipse.org/europa/"> This Europa Discovery Site contains a number of Eclipse based projects diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform.source_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform.source_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml index b9cb4db57..5357a19cc 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform.source_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform.source_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml @@ -10,9 +10,9 @@ %license </license> <url> - <update label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%secondaryUpdateSiteName" url="http://download.eclipse.org/releases/europa"/> + <update label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%secondaryUpdateSiteName" url="https://download.eclipse.org/releases/europa"/> </url> <includes id="org.eclipse.rcp.source" version="3.3.0.v20070607-8y8eE8NEbsN3X_fjWS8HPNG"/> <plugin fragment="false" unpack="false" download-size="0" install-size="0" id="org.eclipse.platform.doc.isv" version="3.3.0.v20070621"/> diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml index e4974b851..8f1ffb44d 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.platform_3.3.0.v20070612-_19UEkLEzwsdF9jSqQ-G/feature.xml @@ -19,9 +19,9 @@ </license> <url> - <update label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%secondaryUpdateSiteName" url="http://download.eclipse.org/releases/europa"/> + <update label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%secondaryUpdateSiteName" url="https://download.eclipse.org/releases/europa"/> </url> <plugin diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.sdk_3.3.0.v20070607-7M7J-BIolz-OcxWxvWAPSfLPqevO/feature.xml b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.sdk_3.3.0.v20070607-7M7J-BIolz-OcxWxvWAPSfLPqevO/feature.xml index 0b59abbde..87abcbbfd 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.sdk_3.3.0.v20070607-7M7J-BIolz-OcxWxvWAPSfLPqevO/feature.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/generator/eclipse3.3/features/org.eclipse.sdk_3.3.0.v20070607-7M7J-BIolz-OcxWxvWAPSfLPqevO/feature.xml @@ -19,9 +19,9 @@ </license> <url> - <update label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%updateSiteName" url="http://update.eclipse.org/updates/3.3"/> - <discovery label="%secondaryUpdateSiteName" url="http://download.eclipse.org/releases/europa"/> + <update label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%updateSiteName" url="https://update.eclipse.org/updates/3.3"/> + <discovery label="%secondaryUpdateSiteName" url="https://download.eclipse.org/releases/europa"/> </url> <includes diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/importexport/unknownformat.p2f b/bundles/org.eclipse.equinox.p2.tests/testData/importexport/unknownformat.p2f index 9ae98bf32..56e172939 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/importexport/unknownformat.p2f +++ b/bundles/org.eclipse.equinox.p2.tests/testData/importexport/unknownformat.p2f @@ -131,7 +131,7 @@ </unit> </units> <repositories> - <repository uri='http://download.eclipse.org/releases/helios'/> + <repository uri='https://download.eclipse.org/releases/helios'/> <repository uri='file:/folk/kzhu0/tmp/plugins/'/> <repository uri='http://hge.javaforge.com/hgeclipse'/> </repositories> diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/mirror/mirrorSourceRepoWithRefs/content.xml b/bundles/org.eclipse.equinox.p2.tests/testData/mirror/mirrorSourceRepoWithRefs/content.xml index 3a3575af8..d53fba1e4 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/mirror/mirrorSourceRepoWithRefs/content.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/mirror/mirrorSourceRepoWithRefs/content.xml @@ -6,10 +6,10 @@ <property name='p2.timestamp' value='1222693511921'/> </properties> <references size='4'> - <repository uri='http://download.eclipse.org/eclipse/updates/3.5/' url='http://download.eclipse.org/eclipse/updates/3.5/' type='0' options='0'/> - <repository uri='http://download.eclipse.org/eclipse/updates/3.5/' url='http://download.eclipse.org/eclipse/updates/3.5/' type='1' options='0'/> - <repository uri='http://download.eclipse.org/eclipse/updates/3.6/' url='http://download.eclipse.org/eclipse/updates/3.6/' type='0' options='0'/> - <repository uri='http://download.eclipse.org/eclipse/updates/3.6/' url='http://download.eclipse.org/eclipse/updates/3.6/' type='1' options='0'/> + <repository uri='https://download.eclipse.org/eclipse/updates/3.5/' url='https://download.eclipse.org/eclipse/updates/3.5/' type='0' options='0'/> + <repository uri='https://download.eclipse.org/eclipse/updates/3.5/' url='https://download.eclipse.org/eclipse/updates/3.5/' type='1' options='0'/> + <repository uri='https://download.eclipse.org/eclipse/updates/3.6/' url='https://download.eclipse.org/eclipse/updates/3.6/' type='0' options='0'/> + <repository uri='https://download.eclipse.org/eclipse/updates/3.6/' url='https://download.eclipse.org/eclipse/updates/3.6/' type='1' options='0'/> </references> <units size='8'> <unit id='helloworldfeature.feature.jar' version='1.0.1'> diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/pausefeature/artifacts.xml b/bundles/org.eclipse.equinox.p2.tests/testData/pausefeature/artifacts.xml index baf533cae..a9f083ecd 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/pausefeature/artifacts.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/pausefeature/artifacts.xml @@ -12,14 +12,14 @@ </properties> <mappings size='5'> <rule filter='(& (classifier=osgi.bundle) (format=packed))' - output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/plugins/${id}_${version}.jar.pack.gz' /> - <rule filter='(& (classifier=osgi.bundle))' output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/plugins/${id}_${version}.jar' /> - <rule filter='(& (classifier=binary))' output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/binary/${id}_${version}' /> + output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/plugins/${id}_${version}.jar.pack.gz' /> + <rule filter='(& (classifier=osgi.bundle))' output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/plugins/${id}_${version}.jar' /> + <rule filter='(& (classifier=binary))' output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/binary/${id}_${version}' /> <rule filter='(& (classifier=org.eclipse.update.feature) (format=packed))' - output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/features/${id}_${version}.jar.pack.gz' /> + output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/features/${id}_${version}.jar.pack.gz' /> <rule filter='(& (classifier=org.eclipse.update.feature))' - output='http://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/features/${id}_${version}.jar' /> + output='https://download.eclipse.org/eclipse/updates/3.7/R-3.7.2-201202080800/features/${id}_${version}.jar' /> </mappings> <artifacts size='13433'> <artifact classifier='binary' diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/pgp/signer2-publickey.asc b/bundles/org.eclipse.equinox.p2.tests/testData/pgp/signer2-publickey.asc new file mode 100644 index 000000000..0218bdce7 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/testData/pgp/signer2-publickey.asc @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGHUyxYBEADATeNx4XA4H2fP9mD5xwlIyh7qvHLezgXpqCwNS9ATqBwnfrCV +06a+pfSLsLoXrP/sdaB5WhijfuxTis18RMfoDVwGMRqyD2GiBCl2vwJDg3BUwHnc +H7W6XkWKO7dkPmF+TUbD3cTWZ7cvrPmMjinmXaq8htuktGuE2VEGZRnuG1m+ChDM +PnSb1ioFS2+MJv13P2fagVk2qC95DkPJGpMk3CY3ghLDEaY/KaJl+8axAlUUUk9N +d3k/KVxxf5k3g26EVQkWWgH2mmolptGO101xW64iked97Cy4NK2yafOF/wmpsavx +PGpOewnDgAJBBPkum6mPH0vcOZgxmLyh4uqfPfr3IaBQlbJLN2dXaDsV83j5t1wZ +2qxOPcWBfORm6W7dC0TQgCXbEG0geMBpJtvnMX83Q2ORqDpjbHRJsV2k+8NxaXON +pYXGr+Lj/9n0xfNEDXhCdGab0XP2tVZ5jfr2OQ5dAomEaPqK5Kq7akoWMddpDLNC +G4IvH8G0cxwruJk00uwd6Nd2NVqVMRYCsBbA89VanUnutLUIpVnnOAetlX9blXHO +JtmiCPGgHyp+iYGhKYVzfuZQyFhonbi0AhidJDvbHsoLT3p4Mcog1B9y6MODBE7R +jwrU+qMqYsYeFhGYKbYyXv9TfEyUvtCQ/GnTtRJAQyicFdOdbK37WecS6QARAQAB +tDdFY2xpcHNlIHAyIHRlc3QgU2lnbmVyIDIgPHNpZ25lcjJAZmFrZXVzZXIuZWNs +aXBzZS5vcmc+iQJYBBMBCABCFiEEzZ1aK4a8T+GDlFHh4vaU9BsKs3AFAmHUyxYC +GwMFCQPCZwAFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJEOL2lPQbCrNw +wH4QAIiCaw1mREgt4ldz7hQvmGxdMuQwVKZPzbOIAlYbZBo0q9SmeMf/CBCO90hg +LFmJmsZV4KUU5NKI7UwkDVrpUCl00Ok6+gtiUTId2tRcwXI+25I478VX27j6OkQj +7Xr6giv8cn8nstt5CF6xxeqrxvpmnZC0u30jE6CL6SdXSd0vViFDPQj3KgGJCRc9 +St+LHB3XJTsadihzQnscqI4E2i5Z3Uj1GogqxtR59M1NAXubl5dySM0qHhwn8O+6 +lcgCCeuyMLLde1M1v8w07jdRUM+IFqHrRnE89EPH3MQeZbQ3UfFXK2r7wx2BJCqL +Irtn68kz834ByKchGR6DqaAw0q+iF2QkgzYxpwai41BgmtUCYnj+HxQNIF4KTzDe +nd8mDAPWttGCoVuV2Tyu9peYOaqyAZ2PZwUEH5MqihPCbenU17RLXzRu/IT/SH47 +NGrN3yKGgLZr2EVWPWFibpoxP4G4NUCHsY75uiL2EWPVSjK/+OOeHIE5k3U3lYwB +7clhBwMkIhQHJ+a0SHRkKixkwrQDw4veKY4LaD0NCBLHFoV5L9orH1ToGM729kr/ ++4I1VQFkL3KvfLjmRbTUgwHeqEquQ96JtqowbNwlpujfHXQKDNsuiKGP5OazXll5 +sH2CR7e4ePqhhzxjLvi9e/79Khq+08eqllS3rs06EXEAJYTo +=807V +-----END PGP PUBLIC KEY BLOCK----- diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/associateSites.xml b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/associateSites.xml index 1238ec316..bddf9e047 100644 --- a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/associateSites.xml +++ b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/associateSites.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> <associateSites> -<associateSite url="http://download.eclipse.org/eclipse/updates/3.5" label="Eclipse Project Update Site"/> +<associateSite url="https://download.eclipse.org/eclipse/updates/3.5" label="Eclipse Project Update Site"/> <associateSite url="This is a bogus URI" label="broken"/> </associateSites>
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.testserver/pom.xml b/bundles/org.eclipse.equinox.p2.testserver/pom.xml index 5310f6302..15046df14 100644 --- a/bundles/org.eclipse.equinox.p2.testserver/pom.xml +++ b/bundles/org.eclipse.equinox.p2.testserver/pom.xml @@ -4,7 +4,7 @@ <parent> <artifactId>org.eclipse.equinox.p2.tests-parent</artifactId> <groupId>org.eclipse</groupId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../../org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.testserver/webfiles/index.html b/bundles/org.eclipse.equinox.p2.testserver/webfiles/index.html index c07f8d265..9f1800e6e 100644 --- a/bundles/org.eclipse.equinox.p2.testserver/webfiles/index.html +++ b/bundles/org.eclipse.equinox.p2.testserver/webfiles/index.html @@ -8,10 +8,10 @@ log in. The credentials are: user: <b>Aladdin</b>, password: <b>open sesame</b>. <a href="http://localhost:8080/private/index.html">here</a></p> <p>The following real content is registered:</p> <ul> -<li>/proxy/private/ - goes to http://http://download.eclipse.org/eclipse/updates/3.4, but requires authentication.</li> -<li>/proxy/public/ - goes to http://http://download.eclipse.org/eclipse/updates/3.4, (useful in redirects).</li> -<li>/proxy/never/ - goes to http://http://download.eclipse.org/eclipse/updates/3.4, but always fail authentication.</li> -<li>/proxy/flipFlop/ - goes to http://http://download.eclipse.org/eclipse/updates/3.4, but fails authentication every second attempt.</li> +<li>/proxy/private/ - goes to https://download.eclipse.org/eclipse/updates/3.4, but requires authentication.</li> +<li>/proxy/public/ - goes to https://download.eclipse.org/eclipse/updates/3.4, (useful in redirects).</li> +<li>/proxy/never/ - goes to https://download.eclipse.org/eclipse/updates/3.4, but always fail authentication.</li> +<li>/proxy/flipFlop/ - goes to https://download.eclipse.org/eclipse/updates/3.4, but fails authentication every second attempt.</li> <li>/proxy/truncated - goes to updates/3.4, but truncates all files</li> <li>/proxy/molested - goes to updates/3.4, but generates gibberish for all files</li> <li>/proxy/decelerate - goes to updates/3.4, but delivers files in very small delayed packets - delay increases.</li> diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.eclipse/pom.xml b/bundles/org.eclipse.equinox.p2.touchpoint.eclipse/pom.xml index 2fb3c4137..7fff2566e 100644 --- a/bundles/org.eclipse.equinox.p2.touchpoint.eclipse/pom.xml +++ b/bundles/org.eclipse.equinox.p2.touchpoint.eclipse/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.touchpoint.natives/META-INF/MANIFEST.MF index fa30088a2..3e794a0db 100644 --- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.touchpoint.natives;singleton:=true -Bundle-Version: 1.4.200.qualifier +Bundle-Version: 1.4.300.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.touchpoint.natives.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/pom.xml b/bundles/org.eclipse.equinox.p2.touchpoint.natives/pom.xml index ccb91e06b..f90831920 100644 --- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/pom.xml +++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.touchpoint.natives</artifactId> - <version>1.4.200-SNAPSHOT</version> + <version>1.4.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties index a7204930b..298d24fa7 100644 --- a/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties +++ b/bundles/org.eclipse.equinox.p2.touchpoint.natives/src/org/eclipse/equinox/internal/p2/touchpoint/natives/messages.properties @@ -33,7 +33,7 @@ BackupStore_manual_restore_needed=Restore failed: {0}. Manual restore of backup BackupStore_missing_backup_directory=Missing backup directory - can not restore: {0} BackupStore_not_a_directory=File is not a directory: {0} BlockMacUpdate_0=Installation impossible -BlockMacUpdate_1=The installation/update you are trying to perform can not be completed because of structural changes to Eclipse. The installation/update will stop and will leave your existing Eclipse installation intact. You need to retrieve a new version of Eclipse from http://download.eclipse.org/. +BlockMacUpdate_1=The installation/update you are trying to perform can not be completed because of structural changes to Eclipse. The installation/update will stop and will leave your existing Eclipse installation intact. You need to retrieve a new version of Eclipse from https://download.eclipse.org/. action_0_status=The action {0} was performed. action_0_failed_on_file_1_reason_2=The action {0} on file {1} failed. Reason: {2} diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF index 9d7b3fde5..31f2492ff 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.transport.ecf -Bundle-Version: 1.3.200.qualifier +Bundle-Version: 1.3.300.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.ecf;bundle-version="3.1.0", org.eclipse.ecf.filetransfer;bundle-version="4.0.0", diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml b/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml index 34c1bcac3..7d8611d3e 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.transport.ecf</artifactId> - <version>1.3.200-SNAPSHOT</version> + <version>1.3.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java index d0e8b4e7b..7b6649c20 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/Activator.java @@ -16,6 +16,7 @@ ******************************************************************************/ package org.eclipse.equinox.internal.p2.transport.ecf; +import java.util.Optional; import org.eclipse.ecf.filetransfer.service.IRetrieveFileTransferFactory; import org.eclipse.ecf.provider.filetransfer.IFileTransferProtocolToFactoryMapper; import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper; @@ -173,4 +174,18 @@ public class Activator implements BundleActivator { return false; } + public static String getProperty(String key) { + if (context != null) { + return context.getProperty(key); + } + return System.getProperty(key); + } + + public static Optional<Version> getVersion() { + return Optional.ofNullable(context) // + .map(BundleContext::getBundle) // + .or(() -> Optional.ofNullable(FrameworkUtil.getBundle(FileReader.class)))// + .map(Bundle::getVersion); + } + } diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java index 8a45c5486..cfca3d832 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileInfoReader.java @@ -163,7 +163,7 @@ public class FileInfoReader extends Job implements IRemoteFileSystemListener { try { container = ContainerFactory.getDefault().createContainer(); } catch (ContainerCreateException e) { - throw RepositoryStatusHelper.fromMessage(Messages.ecf_configuration_error); + throw RepositoryStatusHelper.fromExceptionMessage(e ,Messages.ecf_configuration_error); } IRemoteFileSystemBrowserContainerAdapter adapter = container.getAdapter(IRemoteFileSystemBrowserContainerAdapter.class); diff --git a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java index 97c7e2fb4..7d95289f4 100644 --- a/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java +++ b/bundles/org.eclipse.equinox.p2.transport.ecf/src/org/eclipse/equinox/internal/p2/transport/ecf/FileReader.java @@ -34,7 +34,7 @@ import org.eclipse.equinox.internal.p2.repository.*; import org.eclipse.equinox.internal.p2.repository.Messages; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.osgi.util.NLS; -import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.Version; /** * FileReader is an ECF FileTransferJob implementation. @@ -64,11 +64,11 @@ public final class FileReader extends FileTransferJob implements IFileTransferLi static Map<String, Map<String, String>> options; static private String getProperty(String key, String defaultValue) { - String value = FrameworkUtil.getBundle(FileReader.class).getBundleContext().getProperty(key); - if (value == null) { - value = defaultValue; + String value = Activator.getProperty(key); + if (value != null) { + return value; } - return value; + return defaultValue; } static { @@ -80,7 +80,7 @@ public final class FileReader extends FileTransferJob implements IFileTransferLi String osgiArch = getProperty("org.osgi.framework.processor", "unknownArch");//$NON-NLS-1$//$NON-NLS-2$ String language = getProperty("osgi.nl", "unknownLanguage");//$NON-NLS-1$//$NON-NLS-2$ String osVersion = getProperty("org.osgi.framework.os.version", "unknownOSVersion"); //$NON-NLS-1$ //$NON-NLS-2$ - String p2Version = FrameworkUtil.getBundle(FileReader.class).getVersion().toString(); + String p2Version = Activator.getVersion().map(Version::toString).orElse("unknownVersion"); //$NON-NLS-1$ userAgent = "p2/" + p2Version + " (Java " + javaSpec + ' ' + javaVendor + "; " + osName + ' ' + osVersion + ' ' //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + osgiArch + "; " + language + ") "; //$NON-NLS-1$ //$NON-NLS-2$ String userAgentProvided = getProperty("p2.userAgent", null); //$NON-NLS-1$ diff --git a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/META-INF/MANIFEST.MF index a48a7e72a..78f885414 100644 --- a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: %bundleName Bundle-Vendor: %providerName Bundle-Localization: plugin Bundle-SymbolicName: org.eclipse.equinox.p2.ui.admin.rcp; singleton:=true -Bundle-Version: 1.3.100.qualifier +Bundle-Version: 1.3.200.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.ui.admin.rcp.Activator Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.ui, diff --git a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/pom.xml b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/pom.xml index 9ac0df9f6..be007c637 100644 --- a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.ui.admin.rcp</artifactId> - <version>1.3.100-SNAPSHOT</version> + <version>1.3.200-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/rcp.product b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/rcp.product index 5c61916cd..9ba3bcdc0 100644 --- a/bundles/org.eclipse.equinox.p2.ui.admin.rcp/rcp.product +++ b/bundles/org.eclipse.equinox.p2.ui.admin.rcp/rcp.product @@ -165,7 +165,7 @@ Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in t <plugin id="org.eclipse.core.filesystem.macosx" fragment="true"/> <plugin id="org.eclipse.core.jobs"/> <plugin id="org.eclipse.core.net"/> - <plugin id="org.eclipse.core.net.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.core.net.linux" fragment="true"/> <plugin id="org.eclipse.core.resources"/> <plugin id="org.eclipse.core.runtime"/> <plugin id="org.eclipse.e4.core.commands"/> @@ -239,7 +239,7 @@ Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in t <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.security.macosx" fragment="true"/> <plugin id="org.eclipse.equinox.security.ui"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> diff --git a/bundles/org.eclipse.equinox.p2.ui.admin/pom.xml b/bundles/org.eclipse.equinox.p2.ui.admin/pom.xml index cac9c880a..af1847806 100644 --- a/bundles/org.eclipse.equinox.p2.ui.admin/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.admin/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.ui.discovery/pom.xml b/bundles/org.eclipse.equinox.p2.ui.discovery/pom.xml index 19b456343..54fa0b47d 100644 --- a/bundles/org.eclipse.equinox.p2.ui.discovery/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.discovery/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.ui.importexport/pom.xml b/bundles/org.eclipse.equinox.p2.ui.importexport/pom.xml index c55bcd02f..0d01faecf 100644 --- a/bundles/org.eclipse.equinox.p2.ui.importexport/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.importexport/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/META-INF/MANIFEST.MF index 93ad2243d..f6a0f04ed 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.ui.sdk.scheduler;singleton:=true -Bundle-Version: 1.5.200.qualifier +Bundle-Version: 1.5.300.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.ui.sdk.scheduler.AutomaticUpdatePlugin Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/pom.xml b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/pom.xml index c8360feff..f5f8a49f1 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.ui.sdk.scheduler</artifactId> - <version>1.5.200-SNAPSHOT</version> + <version>1.5.300-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java index 504202512..6d56f31c9 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java +++ b/bundles/org.eclipse.equinox.p2.ui.sdk.scheduler/src/org/eclipse/equinox/internal/p2/ui/sdk/scheduler/AutomaticUpdateScheduler.java @@ -33,6 +33,7 @@ import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.query.IQuery; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.ui.IStartup; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.statushandlers.StatusManager; /** @@ -54,8 +55,6 @@ public class AutomaticUpdateScheduler implements IStartup { private IUpdateListener listener; private IUpdateChecker checker; - private IProvisioningAgent agent; - @Override public void earlyStartup() { AutomaticUpdatePlugin.getDefault().setScheduler(this); @@ -64,7 +63,13 @@ public class AutomaticUpdateScheduler implements IStartup { @Override protected IStatus run(IProgressMonitor monitor) { - agent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), IProvisioningAgent.class); + IProvisioningAgent agent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), + IProvisioningAgent.class); + if (agent == null) { + String message = "No provisioning agent service found"; //$NON-NLS-1$ + AutomaticUpdatePlugin.getDefault().getLog().warn(message, new IllegalStateException(message)); + return Status.CANCEL_STATUS; + } IProfileRegistry registry = agent.getService(IProfileRegistry.class); IProfile currentProfile = registry.getProfile(IProfileRegistry.SELF); if (currentProfile != null @@ -72,7 +77,7 @@ public class AutomaticUpdateScheduler implements IStartup { return Status.OK_STATUS; } - removeUnusedPlugins(registry); + removeUnusedPlugins(registry, agent); scheduleUpdate(); return Status.OK_STATUS; } @@ -81,6 +86,11 @@ public class AutomaticUpdateScheduler implements IStartup { public boolean belongsTo(Object family) { return AutomaticUpdateScheduler.class == family; } + + @Override + public boolean shouldRun() { + return PlatformUI.isWorkbenchRunning(); + } }; updateJob.setSystem(true); @@ -92,8 +102,9 @@ public class AutomaticUpdateScheduler implements IStartup { * Invokes the garbage collector to discard unused plugins, if specified by a * corresponding preference. * + * @param agent non null */ - private void removeUnusedPlugins(IProfileRegistry registry) { + private void removeUnusedPlugins(IProfileRegistry registry, IProvisioningAgent agent) { // check if gc is enabled IPreferenceStore pref = AutomaticUpdatePlugin.getDefault().getPreferenceStore(); if (!pref.getBoolean(PreferenceConstants.PREF_GC_ON_STARTUP)) { @@ -149,10 +160,12 @@ public class AutomaticUpdateScheduler implements IStartup { } }; - IProvisioningAgent pagent = agent; + IProvisioningAgent pagent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), + IProvisioningAgent.class); if (pagent == null) { - // Job not executed yet - pagent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), IProvisioningAgent.class); + String message = "No provisioning agent service found"; //$NON-NLS-1$ + AutomaticUpdatePlugin.getDefault().getLog().warn(message, new IllegalStateException(message)); + return; } checker = pagent.getService(IUpdateChecker.class); if (checker == null) { diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF index 988cfdd1b..4293409db 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.ui.sdk;singleton:=true -Bundle-Version: 1.2.3.qualifier +Bundle-Version: 1.2.4.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.ui.sdk.ProvSDKUIActivator Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -16,15 +16,16 @@ Import-Package: javax.xml.parsers, org.bouncycastle.openpgp;version="1.69.0", org.eclipse.compare;resolution:=optional, org.eclipse.compare.structuremergeviewer;resolution:=optional, + org.eclipse.equinox.internal.p2.artifact.processors.pgp, org.eclipse.equinox.internal.p2.engine.phases, org.eclipse.equinox.p2.core;version="[2.0.0,3.0.0)", - org.eclipse.equinox.internal.p2.artifact.processors.pgp, org.eclipse.equinox.p2.engine;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.engine.query;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.metadata;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.operations;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.planner;version="2.0.0", org.eclipse.equinox.p2.query;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.repository.spi;version="2.0.0", org.eclipse.osgi.util;version="1.1.0", org.osgi.framework;version="1.6.0", org.w3c.dom, diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/pom.xml b/bundles/org.eclipse.equinox.p2.ui.sdk/pom.xml index 32f6d437e..bcca1f5ac 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/pom.xml @@ -4,11 +4,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.ui.sdk</artifactId> - <version>1.2.3-SNAPSHOT</version> + <version>1.2.4-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/ProvSDKMessages.java b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/ProvSDKMessages.java index bf0a1956c..26d979317 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/ProvSDKMessages.java +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/ProvSDKMessages.java @@ -52,12 +52,30 @@ public class ProvSDKMessages extends NLS { public static String RemediationOperation_ResolveJobTask; public static String TrustPreferencePage_title; public static String TrustPreferencePage_export; - public static String TrustPreferencePage_idColumn; - public static String TrustPreferencePage_userColumn; public static String TrustPreferencePage_fileExportTitle; public static String TrustPreferencePage_pgpIntro; public static String TrustPreferencePage_fileImportTitle; public static String TrustPreferencePage_addPGPKeyButtonLabel; + public static String TrustPreferencePage_Contributor; + public static String TrustPreferencePage_CopyFingerprint; + public static String TrustPreferencePage_DataValidExpires; + public static String TrustPreferencePage_DateExpired; + public static String TrustPreferencePage_DateExpiredSince; + public static String TrustPreferencePage_DateNotYetvalid; + public static String TrustPreferencePage_DateNotYetValid; + public static String TrustPreferencePage_DateValid; + public static String TrustPreferencePage_Details; + public static String TrustPreferencePage_Export; + public static String TrustPreferencePage_FingerprintIdColumn; + public static String TrustPreferencePage_NameColumn; + public static String TrustPreferencePage_PreferenceContributor; public static String TrustPreferencePage_removePGPKeyButtonLabel; + public static String TrustPreferencePage_TrustAll; + public static String TrustPreferencePage_TrustAllConfirmationDescription; + public static String TrustPreferencePage_TrustAllConfirmationTitle; + public static String TrustPreferencePage_TrustAllNo; + public static String TrustPreferencePage_TrustAllYes; + public static String TrustPreferencePage_TypeColumn; + public static String TrustPreferencePage_ValidityColumn; } diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/TrustPreferencePage.java b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/TrustPreferencePage.java index 417ba2d68..bb0ef513e 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/TrustPreferencePage.java +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/TrustPreferencePage.java @@ -10,33 +10,60 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.sdk; +import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter; + import java.io.*; -import java.util.ArrayList; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.cert.*; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.*; import java.util.List; +import java.util.function.Function; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPPublicKey; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.*; import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPPublicKeyStore; import org.eclipse.equinox.internal.p2.engine.phases.CertificateChecker; -import org.eclipse.equinox.internal.p2.ui.ProvUIActivator; -import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.equinox.internal.p2.ui.dialogs.PGPPublicKeyViewDialog; +import org.eclipse.equinox.internal.p2.ui.viewers.CertificateLabelProvider; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.engine.IProfileRegistry; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.jface.dialogs.*; +import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.Policy; import org.eclipse.jface.viewers.*; +import org.eclipse.jface.widgets.LabelFactory; +import org.eclipse.jface.widgets.WidgetFactory; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.*; import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; public class TrustPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + private static final String EXPORT_FILTER_PATH = "exportFilterPath"; //$NON-NLS-1$ + private static final String ADD_FILTER_PATH = "addFilterPath"; //$NON-NLS-1$ + private CertificateChecker certificateChecker; + private Set<Certificate> trustedCertificates; private PGPPublicKeyStore trustedKeys; - private boolean dirty = false; + private Map<PGPPublicKey, Set<Bundle>> contributedTrustedKeys; + private boolean dirty; private TableViewer viewer; public TrustPreferencePage() { @@ -50,108 +77,324 @@ public class TrustPreferencePage extends PreferencePage implements IWorkbenchPre @Override protected Control createContents(Composite parent) { + IProvisioningAgent provisioningAgent = ProvSDKUIActivator.getDefault().getProvisioningAgent(); + certificateChecker = new CertificateChecker(provisioningAgent); + certificateChecker + .setProfile(provisioningAgent.getService(IProfileRegistry.class).getProfile(IProfileRegistry.SELF)); + trustedCertificates = new LinkedHashSet<>(certificateChecker.getPreferenceTrustedCertificates()); + contributedTrustedKeys = certificateChecker.getContributedTrustedKeys(); + trustedKeys = certificateChecker.getPreferenceTrustedKeys(); + Composite res = new Composite(parent, SWT.NONE); + res.setLayout(new GridLayout(2, false)); - Label pgpLabel = new Label(res, SWT.WRAP); - pgpLabel.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false, 2, 1)); - pgpLabel.setText(ProvSDKMessages.TrustPreferencePage_pgpIntro); + // Ensure that the message supports wrapping for a long text message. + GridData data = new GridData(SWT.FILL, SWT.DEFAULT, true, false, 2, 1); + data.widthHint = convertWidthInCharsToPixels(90); + LabelFactory factory = WidgetFactory.label(SWT.WRAP).text(ProvSDKMessages.TrustPreferencePage_pgpIntro) + .font(parent.getFont()).layoutData(data); + factory.create(res); - res.setLayout(new GridLayout(2, false)); - viewer = new TableViewer(res); - viewer.getTable().setHeaderVisible(true); + TableColumnLayout tableColumnLayout = new TableColumnLayout(); + Composite tableComposite = WidgetFactory.composite(SWT.NONE) + .layoutData(new GridData(SWT.FILL, SWT.FILL, true, true)).layout(tableColumnLayout).create(res); + Table keyTable = WidgetFactory.table(SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION) + .headerVisible(true).linesVisible(true).font(parent.getFont()).create(tableComposite); + viewer = new TableViewer(keyTable); + keyTable.setHeaderVisible(true); viewer.setContentProvider(new ArrayContentProvider()); - TableViewerColumn idColumn = new TableViewerColumn(viewer, SWT.NONE); - idColumn.setLabelProvider(new ColumnLabelProvider() { - @Override - public String getText(Object element) { - return Long.toHexString(((PGPPublicKey) element).getKeyID()).toUpperCase(); + + // This column is packed later. + TableViewerColumn typeColumn = createColumn(viewer, ProvSDKMessages.TrustPreferencePage_TypeColumn, + key -> "PGP", cert -> "x509", tableColumnLayout, 1); //$NON-NLS-1$ //$NON-NLS-2$ + + createColumn(viewer, ProvSDKMessages.TrustPreferencePage_FingerprintIdColumn, + key -> PGPPublicKeyService.toHexFingerprint(key), + cert -> cert.getSerialNumber().toString(), tableColumnLayout, 10); + + createColumn(viewer, ProvSDKMessages.TrustPreferencePage_NameColumn, key -> { + List<String> userIds = new ArrayList<>(); + key.getUserIDs().forEachRemaining(userIds::add); + return String.join(", ", userIds); //$NON-NLS-1$ + }, cert -> CertificateLabelProvider.getText(cert), tableColumnLayout, 15); + + createColumn(viewer, ProvSDKMessages.TrustPreferencePage_Contributor, key -> { + { + Set<String> contributors = new LinkedHashSet<>(); + if (trustedKeys.all().contains(key)) { + contributors.add(ProvSDKMessages.TrustPreferencePage_PreferenceContributor); + } + Set<Bundle> bundles = contributedTrustedKeys.get(key); + if (bundles != null) { + Set<String> bundleContributors = new TreeSet<>(Policy.getComparator()); + bundles.stream().map(bundle -> getBundleName(bundle)).forEach(bundleContributors::add); + contributors.addAll(bundleContributors); + } + return String.join(", ", contributors); //$NON-NLS-1$ } - }); - idColumn.getColumn().setWidth(16 * 10); // number of chars in a key Id * some heuristic of width - idColumn.getColumn().setText(ProvSDKMessages.TrustPreferencePage_idColumn); - TableViewerColumn userColumn = new TableViewerColumn(viewer, SWT.NONE); - userColumn.setLabelProvider(new ColumnLabelProvider() { - @Override - public String getText(Object element) { - List<String> userIds = new ArrayList<>(); - ((PGPPublicKey) element).getUserIDs().forEachRemaining(userIds::add); - return String.join(",", userIds); //$NON-NLS-1$ + }, cert -> ProvSDKMessages.TrustPreferencePage_PreferenceContributor, tableColumnLayout, + contributedTrustedKeys.isEmpty() ? 8 : 15); + + createColumn(viewer, ProvSDKMessages.TrustPreferencePage_ValidityColumn, pgp -> { + if (pgp.getCreationTime().after(Date.from(Instant.now()))) { + return NLS.bind(ProvSDKMessages.TrustPreferencePage_DateNotYetValid, pgp.getCreationTime()); } - }); - userColumn.getColumn().setWidth(400); - userColumn.getColumn().setText(ProvSDKMessages.TrustPreferencePage_userColumn); - viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - certificateChecker = new CertificateChecker(ProvSDKUIActivator.getDefault().getProvisioningAgent()); - trustedKeys = certificateChecker.buildPGPTrustore(); - viewer.setInput(trustedKeys.all()); + long validSeconds = pgp.getValidSeconds(); + if (validSeconds == 0) { + return ProvSDKMessages.TrustPreferencePage_DateValid; + } + Instant expires = pgp.getCreationTime().toInstant().plus(validSeconds, ChronoUnit.SECONDS); + return expires.isBefore(Instant.now()) + ? NLS.bind(ProvSDKMessages.TrustPreferencePage_DateExpiredSince, expires) + : NLS.bind(ProvSDKMessages.TrustPreferencePage_DataValidExpires, expires); + }, x509 -> { + try { + x509.checkValidity(); + return ProvSDKMessages.TrustPreferencePage_DateValid; + } catch (CertificateExpiredException expired) { + return ProvSDKMessages.TrustPreferencePage_DateExpired; + } catch (CertificateNotYetValidException notYetValid) { + return ProvSDKMessages.TrustPreferencePage_DateNotYetvalid; + } + }, tableColumnLayout, 8); + + updateInput(); + Composite buttonComposite = createVerticalButtonBar(res); buttonComposite.setLayoutData(new GridData(SWT.DEFAULT, SWT.BEGINNING, false, false)); + Button exportButton = new Button(buttonComposite, SWT.PUSH); exportButton.setText(ProvSDKMessages.TrustPreferencePage_export); setVerticalButtonLayoutData(exportButton); + exportButton.setEnabled(false); + exportButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { - PGPPublicKey key = getSelectedKey(); - if (key == null) { - return; - } + Object element = viewer.getStructuredSelection().getFirstElement(); FileDialog dialog = new FileDialog(getShell(), SWT.SAVE); + dialog.setFilterPath(getFilterPath(EXPORT_FILTER_PATH)); dialog.setText(ProvSDKMessages.TrustPreferencePage_fileExportTitle); dialog.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ - dialog.setFileName(Long.toHexString(key.getKeyID()).toUpperCase() + ".asc"); //$NON-NLS-1$ - String path = dialog.open(); - if (path == null) { - return; - } - File destinationFile = new File(path); - try (OutputStream output = new ArmoredOutputStream(new FileOutputStream(destinationFile))) { - output.write(key.getEncoded()); - } catch (IOException ex) { - ProvSDKUIActivator.getDefault().getLog() - .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + FileDialog destination = new FileDialog(exportButton.getShell(), SWT.SAVE); + destination.setFilterPath(getFilterPath(EXPORT_FILTER_PATH)); + destination.setText(ProvSDKMessages.TrustPreferencePage_Export); + if (element instanceof X509Certificate) { + X509Certificate cert = (X509Certificate) element; + destination.setFilterExtensions(new String[] { "*.der" }); //$NON-NLS-1$ + destination.setFileName(cert.getSerialNumber().toString() + ".der"); //$NON-NLS-1$ + String path = destination.open(); + setFilterPath(EXPORT_FILTER_PATH, destination.getFilterPath()); + if (path == null) { + return; + } + File destinationFile = new File(path); + try (FileOutputStream output = new FileOutputStream(destinationFile)) { + output.write(cert.getEncoded()); + } catch (IOException | CertificateEncodingException ex) { + ProvSDKUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvSDKUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } + } else { + PGPPublicKey key = (PGPPublicKey) element; + destination.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ + destination.setFileName(PGPPublicKeyService.toHexFingerprint(key) + ".asc"); //$NON-NLS-1$ + String path = destination.open(); + setFilterPath(EXPORT_FILTER_PATH, destination.getFilterPath()); + if (path == null) { + return; + } + File destinationFile = new File(path); + try (OutputStream output = new ArmoredOutputStream(new FileOutputStream(destinationFile))) { + key.encode(output); + } catch (IOException ex) { + ProvSDKUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvSDKUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } } })); + Button addButton = new Button(buttonComposite, SWT.PUSH); addButton.setText(ProvSDKMessages.TrustPreferencePage_addPGPKeyButtonLabel); addButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { FileDialog dialog = new FileDialog(getShell(), SWT.OPEN); + dialog.setFilterPath(getFilterPath(ADD_FILTER_PATH)); dialog.setText(ProvSDKMessages.TrustPreferencePage_fileImportTitle); - dialog.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ + dialog.setFilterExtensions(new String[] { "*.asc;*.der" }); //$NON-NLS-1$ String path = dialog.open(); + setFilterPath(ADD_FILTER_PATH, dialog.getFilterPath()); if (path == null) { return; } - trustedKeys.add(new File(path)); - viewer.setInput(trustedKeys.all()); + + if (path.endsWith(".der")) { //$NON-NLS-1$ + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$ + try (InputStream input = Files.newInputStream(Paths.get(path))) { + Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(input); + trustedCertificates.addAll(certificates); + updateInput(); + viewer.setSelection(new StructuredSelection(certificates.toArray()), true); + } + } catch (IOException | CertificateException ex) { + ProvSDKUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvSDKUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } + } else { + HashSet<Object> oldKeys = new HashSet<>(trustedKeys.all()); + trustedKeys.add(new File(path)); + + HashSet<Object> newKeys = new HashSet<>(trustedKeys.all()); + newKeys.removeAll(oldKeys); + updateInput(); + viewer.setSelection(new StructuredSelection(newKeys.toArray()), true); + } dirty = true; })); setVerticalButtonLayoutData(addButton); + Button removeButton = new Button(buttonComposite, SWT.PUSH); removeButton.setText(ProvSDKMessages.TrustPreferencePage_removePGPKeyButtonLabel); removeButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { - trustedKeys.remove(getSelectedKey()); - viewer.setInput(trustedKeys.all()); + for (Object key : getSelectedKeys()) { + if (key instanceof PGPPublicKey) { + trustedKeys.remove((PGPPublicKey) key); + } else { + trustedCertificates.remove(key); + } + } + updateInput(); dirty = true; })); + removeButton.setEnabled(false); setVerticalButtonLayoutData(removeButton); + + Runnable details = () -> { + Object element = viewer.getStructuredSelection().getFirstElement(); + if (element instanceof X509Certificate) { + // create and open dialog for certificate chain + CertificateLabelProvider.openDialog(getShell(), (X509Certificate) element); + } else { + new PGPPublicKeyViewDialog(getShell(), (PGPPublicKey) element, + provisioningAgent.getService(PGPPublicKeyService.class)).open(); + } + }; + + Button detailsButton = new Button(buttonComposite, SWT.PUSH); + detailsButton.setText(ProvSDKMessages.TrustPreferencePage_Details); + detailsButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> details.run())); + detailsButton.setEnabled(false); + setVerticalButtonLayoutData(detailsButton); + viewer.addPostSelectionChangedListener(e -> { - exportButton.setEnabled(getSelectedKey() != null); - removeButton.setEnabled(getSelectedKey() != null); + List<Object> selectedKeys = getSelectedKeys(); + exportButton.setEnabled(selectedKeys.size() == 1); + Collection<PGPPublicKey> keys = trustedKeys.all(); + removeButton.setEnabled( + selectedKeys.stream().anyMatch(o -> keys.contains(o) || trustedCertificates.contains(o))); + detailsButton.setEnabled(selectedKeys.size() == 1); }); - exportButton.setEnabled(getSelectedKey() != null); - removeButton.setEnabled(getSelectedKey() != null); + + Button trustAllButton = WidgetFactory.button(SWT.CHECK).text(ProvSDKMessages.TrustPreferencePage_TrustAll) + .font(JFaceResources.getDialogFont()).create(res); + setButtonLayoutData(trustAllButton).verticalSpan = 2; + trustAllButton.setSelection(certificateChecker.isTrustAlways()); + trustAllButton.addSelectionListener(widgetSelectedAdapter(e -> { + if (trustAllButton.getSelection()) { + // Prompt the user to ensure they really understand what they've chosen, the + // risk, and where the preference is stored if they wish to change it in the + // future. Also ensure that the default button is no so that they must + // explicitly click the yes button, not just hit enter. + MessageDialog messageDialog = new MessageDialog(getShell(), + ProvSDKMessages.TrustPreferencePage_TrustAllConfirmationTitle, null, + ProvSDKMessages.TrustPreferencePage_TrustAllConfirmationDescription, MessageDialog.QUESTION, + new String[] { ProvSDKMessages.TrustPreferencePage_TrustAllYes, + ProvSDKMessages.TrustPreferencePage_TrustAllNo }, + 1) { + @Override + public Image getImage() { + return getWarningImage(); + } + }; + int result = messageDialog.open(); + if (result != Window.OK) { + certificateChecker.setTrustAlways(false); + // Restore the setting. + trustAllButton.setSelection(false); + } else { + certificateChecker.setTrustAlways(true); + } + + } + })); + + viewer.addDoubleClickListener(e -> details.run()); + + typeColumn.getColumn().pack(); + + createMenu(); + return res; } - private PGPPublicKey getSelectedKey() { - ISelection sel = viewer.getSelection(); - if (!(sel instanceof IStructuredSelection)) { - return null; - } - Object o = ((IStructuredSelection) sel).getFirstElement(); - if (!(o instanceof PGPPublicKey)) { - return null; - } - return (PGPPublicKey) o; + private void createMenu() { + Control control = viewer.getControl(); + Menu menu = new Menu(control); + control.setMenu(menu); + MenuItem item = new MenuItem(menu, SWT.PUSH); + item.setText(ProvSDKMessages.TrustPreferencePage_CopyFingerprint); + item.addSelectionListener(widgetSelectedAdapter(e -> { + Object element = viewer.getStructuredSelection().getFirstElement(); + if (element instanceof PGPPublicKey) { + Clipboard clipboard = new Clipboard(getShell().getDisplay()); + clipboard.setContents(new Object[] { + PGPPublicKeyService.toHexFingerprint((PGPPublicKey) element) }, + new Transfer[] { TextTransfer.getInstance() }); + clipboard.dispose(); + } + })); + + viewer.addSelectionChangedListener( + e -> item.setEnabled(viewer.getStructuredSelection().getFirstElement() instanceof PGPPublicKey)); + } + + private TableViewerColumn createColumn(TableViewer tableViewer, String text, Function<PGPPublicKey, String> pgpMap, + Function<X509Certificate, String> x509Map, TableColumnLayout tableColumnLayout, int columnWeight) { + TableViewerColumn column = new TableViewerColumn(tableViewer, SWT.NONE); + column.getColumn().setText(text); + column.setLabelProvider(new PGPOrX509ColumnLabelProvider(pgpMap, x509Map)); + + tableColumnLayout.setColumnData(column.getColumn(), new ColumnWeightData(columnWeight)); + return column; + } + + private void updateInput() { + Set<PGPPublicKey> input = new TreeSet<>((k1, k2) -> { + return Arrays.compare(k1.getFingerprint(), k2.getFingerprint()); + }); + input = new LinkedHashSet<>(); + Collection<PGPPublicKey> all = trustedKeys.all(); + input = new TreeSet<>((k1, k2) -> { + boolean contains1 = all.contains(k1); + boolean contains2 = all.contains(k2); + if (contains1 != contains2) { + if (contains1) { + return -1; + } + return 1; + } + return PGPPublicKeyService.toHexFingerprint(k1).compareTo(PGPPublicKeyService.toHexFingerprint(k2)); + }); + input.addAll(all); + input.addAll(contributedTrustedKeys.keySet()); + + LinkedHashSet<Object> allInput = new LinkedHashSet<>(); + allInput.addAll(trustedCertificates); + allInput.addAll(input); + viewer.setInput(allInput); + } + + @SuppressWarnings("unchecked") + private List<Object> getSelectedKeys() { + return viewer.getStructuredSelection().toList(); } private Composite createVerticalButtonBar(Composite parent) { @@ -181,12 +424,68 @@ public class TrustPreferencePage extends PreferencePage implements IWorkbenchPre return data; } + private String getFilterPath(String key) { + IDialogSettings dialogSettings = DialogSettings + .getOrCreateSection(ProvSDKUIActivator.getDefault().getDialogSettings(), getClass().getName()); + String filterPath = dialogSettings.get(key); + if (filterPath == null) { + filterPath = System.getProperty("user.home"); //$NON-NLS-1$ + } + return filterPath; + } + + private void setFilterPath(String key, String filterPath) { + if (filterPath != null) { + IDialogSettings dialogSettings = DialogSettings + .getOrCreateSection(ProvSDKUIActivator.getDefault().getDialogSettings(), getClass().getName()); + dialogSettings.put(key, filterPath); + } + } + + private String getBundleName(Bundle bundle) { + String value = bundle.getHeaders().get(Constants.BUNDLE_NAME); + return value == null ? bundle.getSymbolicName() : Platform.getResourceString(bundle, value); + } + + @Override + protected void performDefaults() { + trustedCertificates = new LinkedHashSet<>(certificateChecker.getPreferenceTrustedCertificates()); + trustedKeys = certificateChecker.getPreferenceTrustedKeys(); + updateInput(); + super.performDefaults(); + } + @Override public boolean performOk() { if (dirty) { - return certificateChecker.persistTrustedKeys(trustedKeys).isOK(); + IStatus persistTrustedCertificates = certificateChecker.persistTrustedCertificates(trustedCertificates); + IStatus persistTrustedKeys = certificateChecker.persistTrustedKeys(trustedKeys); + dirty = false; + return persistTrustedKeys.isOK() && persistTrustedCertificates.isOK(); } return true; } + private static class PGPOrX509ColumnLabelProvider extends ColumnLabelProvider { + private Function<PGPPublicKey, String> pgpMap; + private Function<X509Certificate, String> x509map; + + public PGPOrX509ColumnLabelProvider(Function<PGPPublicKey, String> pgpMap, + Function<X509Certificate, String> x509map) { + this.pgpMap = pgpMap; + this.x509map = x509map; + } + + @Override + public String getText(Object element) { + if (element instanceof PGPPublicKey) { + return pgpMap.apply((PGPPublicKey) element); + } + if (element instanceof X509Certificate) { + return x509map.apply((X509Certificate) element); + } + return super.getText(element); + } + } + } diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/messages.properties b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/messages.properties index 0e344af3d..93df84f7f 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/messages.properties +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/messages.properties @@ -37,12 +37,28 @@ RemediationOperation_ResolveJobName=Searching alternate solutions... RemediationOperation_ResolveJobTask=Some items cannot be at the highest version. Searching for the highest common denominator ... TrustPreferencePage_title=Trust TrustPreferencePage_export=\uD83D\uDCE5 E&xport... -TrustPreferencePage_idColumn=Id -TrustPreferencePage_userColumn=User TrustPreferencePage_fileExportTitle=Export PGP public key TrustPreferencePage_fileImportTitle=Import PGP public key to trust TrustPreferencePage_addPGPKeyButtonLabel=\u2795 &Add... +TrustPreferencePage_Contributor=Contributor +TrustPreferencePage_CopyFingerprint=Copy Fingerprint +TrustPreferencePage_DataValidExpires=\u2714\uFE0F Valid, expires {0} +TrustPreferencePage_DateExpired=\u274C Expired +TrustPreferencePage_DateExpiredSince=\u274C Expired since {0} +TrustPreferencePage_DateNotYetvalid=\u274C Not yet valid +TrustPreferencePage_DateNotYetValid=\u274C Not yet valid, starts {0} +TrustPreferencePage_DateValid=\u2714\uFE0F Valid +TrustPreferencePage_Details=\uD83D\uDD0D D&etails... +TrustPreferencePage_Export=Export... +TrustPreferencePage_FingerprintIdColumn=Fingerprint/Id +TrustPreferencePage_NameColumn=Name +TrustPreferencePage_PreferenceContributor=<preferences> TrustPreferencePage_removePGPKeyButtonLabel=\uD83D\uDDD1\uFE0F &Remove - -TrustPreferencePage_pgpIntro=The following PGP public keys are considered as trusted.\n\ -Artifacts that are signed and verified by one of those keys will be trusted and installed without further trust confirmation request.
\ No newline at end of file +TrustPreferencePage_pgpIntro=Artifacts verifiably signed by the following trusted certificates and PGP public keys will be installed without confirmation. +TrustPreferencePage_TrustAll=Trust all content +TrustPreferencePage_TrustAllConfirmationDescription=Are you certain you wish to trust all content, including unsigned content of unknown origin, with no confirmation for all future operations? +TrustPreferencePage_TrustAllConfirmationTitle=Always Trust Everything Confirmation +TrustPreferencePage_TrustAllNo=No, Prompt Me Instead +TrustPreferencePage_TrustAllYes=Yes, I Accept the Risk +TrustPreferencePage_TypeColumn=Type +TrustPreferencePage_ValidityColumn=Validity Dates diff --git a/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF index 429c0b980..433d2e820 100644 --- a/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %bundleName Bundle-SymbolicName: org.eclipse.equinox.p2.ui;singleton:=true -Bundle-Version: 2.7.300.qualifier +Bundle-Version: 2.7.400.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.ui.ProvUIActivator Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -19,6 +19,7 @@ Export-Package: org.eclipse.equinox.internal.p2.ui; org.eclipse.equinox.internal.p2.ui.dialogs; x-friends:="org.eclipse.equinox.p2.ui.admin, org.eclipse.equinox.p2.ui.sdk.scheduler, + org.eclipse.equinox.p2.ui.sdk, org.eclipse.pde.ui, org.eclipse.equinox.p2.ui.importexport", org.eclipse.equinox.internal.p2.ui.model; @@ -28,7 +29,7 @@ Export-Package: org.eclipse.equinox.internal.p2.ui; org.eclipse.equinox.p2.ui.sdk, org.eclipse.equinox.p2.ui.importexport", org.eclipse.equinox.internal.p2.ui.query;x-friends:="org.eclipse.equinox.internal.p2.ui.analysis,org.eclipse.equinox.p2.ui.admin", - org.eclipse.equinox.internal.p2.ui.viewers;x-friends:="org.eclipse.equinox.p2.ui.admin,org.eclipse.equinox.p2.ui.sdk.scheduler,org.eclipse.equinox.p2.ui.importexport", + org.eclipse.equinox.internal.p2.ui.viewers;x-friends:="org.eclipse.equinox.p2.ui.admin,org.eclipse.equinox.p2.ui.sdk,org.eclipse.equinox.p2.ui.sdk.scheduler,org.eclipse.equinox.p2.ui.importexport", org.eclipse.equinox.p2.ui;version="2.6.0" Require-Bundle: org.eclipse.ui;bundle-version="3.107.0", org.eclipse.core.runtime;bundle-version="[3.18.0,4.0.0)", @@ -39,9 +40,11 @@ Import-Package: javax.xml.parsers, org.bouncycastle.bcpg;version="1.65.0", org.bouncycastle.openpgp;version="1.65.0", org.bouncycastle.util;version="1.65.1", + org.eclipse.equinox.internal.p2.artifact.processors.pgp, org.eclipse.equinox.internal.p2.artifact.repository, org.eclipse.equinox.internal.p2.core.helpers, org.eclipse.equinox.internal.p2.director, + org.eclipse.equinox.internal.p2.extensionlocation, org.eclipse.equinox.internal.p2.metadata, org.eclipse.equinox.internal.p2.metadata.repository, org.eclipse.equinox.internal.p2.operations, @@ -60,7 +63,9 @@ Import-Package: javax.xml.parsers, org.eclipse.equinox.p2.query;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.artifact;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.repository.artifact.spi;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository.metadata;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.repository.spi;version="2.0.0", org.osgi.framework;version="1.6.0", org.osgi.service.packageadmin;version="1.2.0", org.w3c.dom, diff --git a/bundles/org.eclipse.equinox.p2.ui/plugin.xml b/bundles/org.eclipse.equinox.p2.ui/plugin.xml index 6f6031e2e..87e8645fd 100644 --- a/bundles/org.eclipse.equinox.p2.ui/plugin.xml +++ b/bundles/org.eclipse.equinox.p2.ui/plugin.xml @@ -12,6 +12,13 @@ <adapter type="org.eclipse.equinox.p2.repository.metadata.IMetadataRepository"/> <adapter type="org.eclipse.equinox.p2.repository.artifact.IArtifactRepository"/> </factory> + <factory + adaptableType="org.eclipse.ui.internal.about.AboutPluginsPage" + class="org.eclipse.equinox.internal.p2.ui.KeySigningInfoFactory"> + <adapter + type="org.eclipse.ui.internal.about.AboutBundleData$ExtendedSigningInfo"> + </adapter> + </factory> </extension> <extension diff --git a/bundles/org.eclipse.equinox.p2.ui/pom.xml b/bundles/org.eclipse.equinox.p2.ui/pom.xml index 027ad5f4a..13cace16e 100644 --- a/bundles/org.eclipse.equinox.p2.ui/pom.xml +++ b/bundles/org.eclipse.equinox.p2.ui/pom.xml @@ -14,11 +14,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.ui</artifactId> - <version>2.7.300-SNAPSHOT</version> + <version>2.7.400-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/IProvHelpContextIds.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/IProvHelpContextIds.java index cba53aa52..3c75aa5e9 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/IProvHelpContextIds.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/IProvHelpContextIds.java @@ -13,35 +13,37 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui; - /** * Help context ids for the P2 UI * <p> * This interface contains constants only; it is not intended to be implemented * or extended. * </p> + * * @since 3.4 * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. */ public interface IProvHelpContextIds { - public static final String PREFIX = ProvUIActivator.PLUGIN_ID + "."; //$NON-NLS-1$ + String PREFIX = ProvUIActivator.PLUGIN_ID + "."; //$NON-NLS-1$ + + String REVERT_CONFIGURATION_WIZARD = PREFIX + "revert_configuration_wizard_context"; //$NON-NLS-1$ - public static final String REVERT_CONFIGURATION_WIZARD = PREFIX + "revert_configuration_wizard_context"; //$NON-NLS-1$ + String UNINSTALL_WIZARD = PREFIX + "uinstall_wizard_context"; //$NON-NLS-1$ - public static final String UNINSTALL_WIZARD = PREFIX + "uinstall_wizard_context"; //$NON-NLS-1$ + String UPDATE_WIZARD = PREFIX + "update_wizard_context"; //$NON-NLS-1$ - public static final String UPDATE_WIZARD = PREFIX + "update_wizard_context"; //$NON-NLS-1$ + String ADD_REPOSITORY_DIALOG = PREFIX + "add_repository_dialog_context"; //$NON-NLS-1$ - public static final String ADD_REPOSITORY_DIALOG = PREFIX + "add_repository_dialog_context"; //$NON-NLS-1$ + String INSTALL_WIZARD = PREFIX + "install_wizard_context"; //$NON-NLS-1$ - public static final String INSTALL_WIZARD = PREFIX + "install_wizard_context"; //$NON-NLS-1$ + String REPOSITORY_MANIPULATION_DIALOG = PREFIX + "repository_manipulation_dialog_context"; //$NON-NLS-1$ - public static final String REPOSITORY_MANIPULATION_DIALOG = PREFIX + "repository_manipulation_dialog_context"; //$NON-NLS-1$ + String INSTALLED_SOFTWARE = PREFIX + "installed_software_context"; //$NON-NLS-1$ - public static final String INSTALLED_SOFTWARE = PREFIX + "installed_software_context"; //$NON-NLS-1$ + String AVAILABLE_SOFTWARE = PREFIX + "available_software_context"; //$NON-NLS-1$ - public static final String AVAILABLE_SOFTWARE = PREFIX + "available_software_context"; //$NON-NLS-1$ + String TRUST_DIALOG = PREFIX + "trust_dialog_context"; //$NON-NLS-1$ } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/KeySigningInfoFactory.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/KeySigningInfoFactory.java new file mode 100644 index 000000000..1134dbb56 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/KeySigningInfoFactory.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.ui; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import org.bouncycastle.openpgp.*; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPPublicKeyStore; +import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.equinox.p2.query.IQueryResult; +import org.eclipse.equinox.p2.repository.artifact.*; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.ui.internal.about.AboutBundleData; +import org.eclipse.ui.internal.about.AboutPluginsPage; +import org.osgi.framework.Bundle; + +/** + * A factory used by the {@link AboutPluginsPage} to provide extended signing + * information, in particular information about PGP signing. + * + * @since 2.7.400 + */ +public class KeySigningInfoFactory implements IAdapterFactory { + + private static final Class<?>[] CLASSES = new Class[] { AboutBundleData.ExtendedSigningInfo.class }; + + @Override + public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) { + if (adapterType == AboutBundleData.ExtendedSigningInfo.class) { + return adapterType.cast(new AboutBundleData.ExtendedSigningInfo() { + private final Map<File, Map<PGPSignature, PGPPublicKey>> bundlePoolArtficactSigningDetails = getBundlePoolArtficactPGPSigningDetails(); + + @Override + public boolean isSigned(Bundle bundle) { + return getDetails(bundle) != null; + } + + @Override + public String getSigningType(Bundle bundle) { + return ProvUIMessages.KeySigningInfoFactory_PGPSigningType; + } + + @Override + public String getSigningDetails(Bundle bundle) { + Map<PGPSignature, PGPPublicKey> details = getDetails(bundle); + if (details != null) { + PGPPublicKeyService keyService = getKeyService(); + List<String> lines = new ArrayList<>(); + for (PGPPublicKey key : details.values()) { + if (keyService != null) { + // Be sure to normalize/enhance the key so we properly don't show + // self-signatures. + key = keyService.addKey(key); + } + if (!lines.isEmpty()) { + lines.add(""); //$NON-NLS-1$ + } + addDetails(key, lines, ""); //$NON-NLS-1$ + if (keyService != null) { + Set<PGPPublicKey> verifiedCertifications = keyService.getVerifiedCertifications(key); + boolean first = true; + for (PGPPublicKey verifyingKey : verifiedCertifications) { + /// Don't show self-signatures. + if (!verifyingKey.equals(key)) { + if (first) { + lines.add(" " + ProvUIMessages.KeySigningInfoFactory_KeySignersSection); //$NON-NLS-1$ + first = false; + } + addDetails(verifyingKey, lines, " "); //$NON-NLS-1$ + } + } + } + } + return String.join("\n", lines); //$NON-NLS-1$ + } + return null; + } + + @Override + public Date getSigningTime(Bundle bundle) { + Map<PGPSignature, PGPPublicKey> details = getDetails(bundle); + return details == null ? null : details.keySet().iterator().next().getCreationTime(); + } + + private void addDetails(PGPPublicKey key, List<String> lines, String indentation) { + lines.add(indentation + ProvUIMessages.KeySigningInfoFactory_FingerprintItem + + PGPPublicKeyService.toHexFingerprint(key)); + for (Iterator<String> userIDs = key.getUserIDs(); userIDs.hasNext();) { + lines.add(indentation + ProvUIMessages.KeySigningInfoFactory_UserIDItem + userIDs.next()); + } + } + + private PGPPublicKeyService getKeyService() { + IProvisioningAgent agent = org.eclipse.equinox.internal.p2.extensionlocation.Activator + .getCurrentAgent(); + return agent == null ? null : agent.getService(PGPPublicKeyService.class); + } + + private Map<PGPSignature, PGPPublicKey> getDetails(Bundle bundle) { + try { + File bundleFile = FileLocator.getBundleFile(bundle).getCanonicalFile(); + Map<PGPSignature, PGPPublicKey> details = bundlePoolArtficactSigningDetails.get(bundleFile); + return details; + } catch (IOException | RuntimeException e) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); + return null; + } + } + }); + } + + return null; + } + + @Override + public Class<?>[] getAdapterList() { + return CLASSES; + } + + /** + * Returns a map from artifact files to the PGP signature/key pairs that were + * used to {@link PGPSignatureVerifier#close() verify} the artifact while it was + * being downloaded. + * + * @return a map of all the PGP signed artifact files to their signature/key + * pairs. + */ + private static Map<File, Map<PGPSignature, PGPPublicKey>> getBundlePoolArtficactPGPSigningDetails() { + Map<File, Map<PGPSignature, PGPPublicKey>> result = new LinkedHashMap<>(); + try { + // Look up artifact metadata for all the bundle pool repository of the + // installation. + IFileArtifactRepository bundlePoolRepository = org.eclipse.equinox.internal.p2.extensionlocation.Activator + .getBundlePoolRepository(); + IQueryResult<IArtifactKey> allArtifactKeys = bundlePoolRepository.query(ArtifactKeyQuery.ALL_KEYS, + new NullProgressMonitor()); + for (IArtifactKey key : allArtifactKeys) { + for (IArtifactDescriptor descriptor : bundlePoolRepository.getArtifactDescriptors(key)) { + File file = bundlePoolRepository.getArtifactFile(descriptor); + if (file != null) { + try { + Collection<PGPSignature> signatures = PGPSignatureVerifier.getSignatures(descriptor); + if (!signatures.isEmpty()) { + Map<PGPSignature, PGPPublicKey> details = new LinkedHashMap<>(); + PGPPublicKeyStore keys = PGPSignatureVerifier.getKeys(descriptor); + for (PGPSignature signature : signatures) { + Collection<PGPPublicKey> signingKeys = keys.getKeys(signature.getKeyID()); + if (!signingKeys.isEmpty()) { + // There is vanishingly small chance that two keys with colliding key IDs were + // used on two different signatures on the same artifact. + details.put(signature, signingKeys.iterator().next()); + } + } + if (!details.isEmpty()) { + result.put(file.getCanonicalFile(), details); + } + } + } catch (IOException | PGPException | RuntimeException e) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); + } + } + } + } + } catch (RuntimeException e) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); + } + return result; + } +} diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java index bcee1d0ef..f79aacc0b 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java @@ -78,6 +78,7 @@ public class ProvUIMessages extends NLS { public static String IUGeneralInfoPropertyPage_VersionLabel; public static String IULicensePropertyPage_NoLicense; public static String IULicensePropertyPage_ViewLicenseLabel; + public static String PGPPublicKeyViewDialog_Title; public static String ProfileModificationAction_InvalidSelections; public static String ProfileModificationWizardPage_DetailsLabel; public static String ProfileSnapshots_Label; @@ -247,6 +248,10 @@ public class ProvUIMessages extends NLS { public static String SelectableIUsPage_Deselect_All; public static String InstallRemediationPage_Title; public static String InstallRemediationPage_Description; + public static String KeySigningInfoFactory_FingerprintItem; + public static String KeySigningInfoFactory_KeySignersSection; + public static String KeySigningInfoFactory_PGPSigningType; + public static String KeySigningInfoFactory_UserIDItem; public static String UpdateRemediationPage_Title; public static String UpdateRemediationPage_Description; public static String RemediationPage_SubDescription; @@ -274,12 +279,35 @@ public class ProvUIMessages extends NLS { public static String TrustCertificateDialog_Export; public static String TrustCertificateDialog_Title; public static String TrustCertificateDialog_Message; + public static String TrustCertificateDialog_MessageUnsigned; + public static String TrustCertificateDialog_MessageNameWarning; + public static String TrustCertificateDialog_MessagePGP; public static String TrustCertificateDialog_AcceptSelectedButtonLabel; + public static String TrustCertificateDialog_AlwaysTrust; + public static String TrustCertificateDialog_AlwaysTrustConfirmationMessage; + public static String TrustCertificateDialog_AlwaysTrustConfirmationTitle; + public static String TrustCertificateDialog_AlwaysTrustNo; + public static String TrustCertificateDialog_AlwaysTrustYes; + public static String TrustCertificateDialog_ArtifactId; public static String TrustCertificateDialog_SelectAll; public static String TrustCertificateDialog_DeselectAll; public static String TrustCertificateDialog_ObjectType; public static String TrustCertificateDialog_Id; public static String TrustCertificateDialog_Name; + public static String TrustCertificateDialog_Classifier; + public static String TrustCertificateDialog_CopyFingerprint; + public static String TrustCertificateDialog_dates; + public static String TrustCertificateDialog_NotApplicable; + public static String TrustCertificateDialog_NotYetValidStartDate; + public static String TrustCertificateDialog_expiredSince; + public static String TrustCertificateDialog_validExpires; + public static String TrustCertificateDialog_valid; + public static String TrustCertificateDialog_expired; + public static String TrustCertificateDialog_notYetValid; + public static String TrustCertificateDialog_RememberSigners; + public static String TrustCertificateDialog_Unknown; + public static String TrustCertificateDialog_Unsigned; + public static String TrustCertificateDialog_Version; // Operations public static String UpdateManagerCompatibility_ExportSitesTitle; public static String UpdateManagerCompatibility_ImportSitesTitle; diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ServiceUIComponent.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ServiceUIComponent.java index fcea5da5a..cad8f35b9 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ServiceUIComponent.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ServiceUIComponent.java @@ -13,9 +13,8 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui; -import org.eclipse.equinox.p2.core.UIServices; - import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.core.UIServices; import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory; /** @@ -26,7 +25,7 @@ public class ServiceUIComponent implements IAgentServiceFactory { @Override public Object createService(IProvisioningAgent agent) { - return new ValidationDialogServiceUI(); + return new ValidationDialogServiceUI(agent); } } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java index 482fe4844..ae451f4c7 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java @@ -14,19 +14,29 @@ *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui; +import java.io.File; import java.net.URL; import java.security.cert.Certificate; import java.util.*; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; import org.bouncycastle.openpgp.PGPPublicKey; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.equinox.internal.p2.ui.dialogs.TrustCertificateDialog; import org.eclipse.equinox.internal.p2.ui.dialogs.UserValidationDialog; +import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.core.UIServices; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.equinox.p2.repository.artifact.spi.IArtifactUIServices; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; import org.eclipse.equinox.p2.ui.LoadMetadataRepositoryJob; import org.eclipse.jface.dialogs.*; import org.eclipse.jface.viewers.TreeNode; +import org.eclipse.jface.window.IShellProvider; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; @@ -40,16 +50,45 @@ import org.eclipse.ui.PlatformUI; * declaration is made in the serviceui_component.xml file. * */ -public class ValidationDialogServiceUI extends UIServices { +public class ValidationDialogServiceUI extends UIServices implements IArtifactUIServices { + + private final IProvisioningAgent agent; + + private Display display; + + private Consumer<String> linkHandler = link -> { + if (PlatformUI.isWorkbenchRunning()) { + try { + URL url = new URL(link); + PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser().openURL(url); + } catch (Exception x) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, x.getMessage(), x)); + } + } + }; + + private IShellProvider shellProvider = () -> ProvUI.getDefaultParentShell(); + + public ValidationDialogServiceUI() { + this(null); + } + + public ValidationDialogServiceUI(IProvisioningAgent agent) { + this.agent = agent; + } static final class MessageDialogWithLink extends MessageDialog { private final String linkText; + private final Consumer<String> linkOpener; MessageDialogWithLink(Shell parentShell, String dialogTitle, Image dialogTitleImage, String dialogMessage, - int dialogImageType, String[] dialogButtonLabels, int defaultIndex, String linkText) { + int dialogImageType, String[] dialogButtonLabels, int defaultIndex, String linkText, + Consumer<String> linkOpener) { super(parentShell, dialogTitle, dialogTitleImage, dialogMessage, dialogImageType, dialogButtonLabels, defaultIndex); this.linkText = linkText; + this.linkOpener = linkOpener; } @Override @@ -60,13 +99,7 @@ public class ValidationDialogServiceUI extends UIServices { Link link = new Link(parent, SWT.NONE); link.setText(linkText); link.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { - try { - URL url = new URL(e.text); - PlatformUI.getWorkbench().getBrowserSupport().getExternalBrowser().openURL(url); - } catch (Exception x) { - ProvUIActivator.getDefault().getLog().log(// - new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, x.getMessage(), x)); - } + linkOpener.accept(e.text); })); return link; } @@ -96,8 +129,8 @@ public class ValidationDialogServiceUI extends UIServices { final AuthenticationInfo[] result = new AuthenticationInfo[1]; if (!suppressAuthentication() && !isHeadless()) { - PlatformUI.getWorkbench().getDisplay().syncExec(() -> { - Shell shell = ProvUI.getDefaultParentShell(); + getDisplay().syncExec(() -> { + Shell shell = shellProvider.getShell(); String message = NLS.bind(ProvUIMessages.ServiceUI_LoginDetails, location); UserValidationDialog dialog = new UserValidationDialog(shell, ProvUIMessages.ServiceUI_LoginRequired, null, message); @@ -141,10 +174,10 @@ public class ValidationDialogServiceUI extends UIServices { // detail should be trusted if (!isHeadless() && unsignedDetail != null && unsignedDetail.length > 0) { final boolean[] result = new boolean[] { false }; - PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { + getDisplay().syncExec(new Runnable() { @Override public void run() { - Shell shell = ProvUI.getDefaultParentShell(); + Shell shell = shellProvider.getShell(); OkCancelErrorDialog dialog = new OkCancelErrorDialog(shell, ProvUIMessages.ServiceUI_warning_title, null, createStatus(), IStatus.WARNING); result[0] = dialog.open() == IDialogConstants.OK_ID; @@ -169,17 +202,46 @@ public class ValidationDialogServiceUI extends UIServices { // We've established trust for unsigned content, now examine the untrusted // chains if (!isHeadless() && (untrustedChains.length > 0 || !untrustedPublicKeys.isEmpty())) { - final TrustCertificateDialog[] dialog = new TrustCertificateDialog[1]; - final TreeNode[] input = createTreeNodes(untrustedChains, untrustedPublicKeys); - PlatformUI.getWorkbench().getDisplay().syncExec(() -> { - Shell shell = ProvUI.getDefaultParentShell(); - TrustCertificateDialog trustCertificateDialog = new TrustCertificateDialog(shell, input); - dialog[0] = trustCertificateDialog; - trustCertificateDialog.open(); - }); - persistTrust = true; - if (dialog[0].getResult() != null) { - for (Object o : dialog[0].getResult()) { + return getTrustInfo( + Arrays.stream(untrustedChains).collect( + Collectors.toMap(Arrays::asList, it -> Set.of(), (e1, e2) -> e1, LinkedHashMap::new)), + untrustedPublicKeys.stream().collect( + Collectors.toMap(Function.identity(), it -> Set.of(), (e1, e2) -> e1, LinkedHashMap::new)), + null, null); + + } + + return new TrustInfo(trustedCertificates, trustedKeys, persistTrust, trustUnsigned); + } + + @Override + public TrustInfo getTrustInfo(Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificates, + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifacts) { + boolean trustUnsigned = true; + AtomicBoolean persistTrust = new AtomicBoolean(); + AtomicBoolean trustAlways = new AtomicBoolean(); + List<Certificate> trustedCertificates = new ArrayList<>(); + List<PGPPublicKey> trustedKeys = new ArrayList<>(); + if (!isHeadless()) { + TreeNode[] input = createTreeNodes(untrustedCertificates, untrustedPGPKeys, unsignedArtifacts, artifacts); + if (input.length != 0) { + trustUnsigned = unsignedArtifacts == null || unsignedArtifacts.isEmpty(); + List<Object> result = new ArrayList<>(); + getDisplay().syncExec(() -> { + Shell shell = shellProvider.getShell(); + TrustCertificateDialog trustCertificateDialog = new TrustCertificateDialog(shell, input); + if (trustCertificateDialog.open() == Window.OK) { + Object[] dialogResult = trustCertificateDialog.getResult(); + if (dialogResult != null) { + result.addAll(Arrays.asList(dialogResult)); + } + persistTrust.set(trustCertificateDialog.isRememberSelectedSigners()); + trustAlways.set(trustCertificateDialog.isTrustAlways()); + } + }); + for (Object o : result) { if (o instanceof TreeNode) { o = ((TreeNode) o).getValue(); } @@ -187,40 +249,93 @@ public class ValidationDialogServiceUI extends UIServices { trustedCertificates.add((Certificate) o); } else if (o instanceof PGPPublicKey) { trustedKeys.add((PGPPublicKey) o); + } else if (o == null) { + trustUnsigned = true; } } } } - return new TrustInfo(trustedCertificates, trustedKeys, persistTrust, trustUnsigned); + + return new TrustInfo(trustedCertificates, trustedKeys, persistTrust.get(), trustUnsigned, trustAlways.get()); } - private TreeNode[] createTreeNodes(Certificate[][] certificates, Collection<PGPPublicKey> untrustedPublicKeys) { - TreeNode[] children = new TreeNode[certificates.length + untrustedPublicKeys.size()]; - int i = 0; - for (i = 0; i < certificates.length; i++) { - TreeNode head = new TreeNode(certificates[i][0]); - TreeNode parent = head; - children[i] = head; - for (int j = 0; j < certificates[i].length; j++) { - TreeNode node = new TreeNode(certificates[i][j]); - node.setParent(parent); - parent.setChildren(new TreeNode[] { node }); - parent = node; + private TreeNode[] createTreeNodes(Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificates, + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifacts) { + + List<ExtendedTreeNode> children = new ArrayList<>(); + if (untrustedCertificates != null && !untrustedCertificates.isEmpty()) { + for (Map.Entry<List<Certificate>, Set<IArtifactKey>> entry : untrustedCertificates.entrySet()) { + ExtendedTreeNode parent = null; + List<Certificate> key = entry.getKey(); + Set<IArtifactKey> associatedArtifacts = entry.getValue(); + for (Certificate certificate : key) { + ExtendedTreeNode node = new ExtendedTreeNode(certificate, associatedArtifacts); + if (parent == null) { + children.add(node); + } else { + node.setParent(parent); + parent.setChildren(new TreeNode[] { node }); + } + parent = node; + } + } + } + + if (untrustedPGPKeys != null && !untrustedPGPKeys.isEmpty()) { + PGPPublicKeyService keyService = agent == null ? null : agent.getService(PGPPublicKeyService.class); + for (Map.Entry<PGPPublicKey, Set<IArtifactKey>> entry : untrustedPGPKeys.entrySet()) { + PGPPublicKey key = entry.getKey(); + Set<IArtifactKey> associatedArtifacts = entry.getValue(); + ExtendedTreeNode node = new ExtendedTreeNode(key, associatedArtifacts); + children.add(node); + expandChildren(node, key, keyService, new HashSet<>(), Integer.getInteger("p2.pgp.trust.depth", 3)); //$NON-NLS-1$ } } - for (PGPPublicKey key : untrustedPublicKeys) { - children[i] = new TreeNode(key); - i++; + + if (unsignedArtifacts != null && !unsignedArtifacts.isEmpty()) { + ExtendedTreeNode node = new ExtendedTreeNode(null, unsignedArtifacts); + children.add(node); + + } + + return children.toArray(TreeNode[]::new); + } + + private void expandChildren(TreeNode result, PGPPublicKey key, PGPPublicKeyService keyService, + Set<PGPPublicKey> visited, int remainingDepth) { + if (keyService != null && remainingDepth > 0 && visited.add(key)) { + Set<PGPPublicKey> certifications = keyService.getVerifiedCertifications(key); + if (certifications != null && !certifications.isEmpty()) { + List<TreeNode> children = new ArrayList<>(); + for (PGPPublicKey certifyingKey : certifications) { + if (visited.add(certifyingKey)) { + TreeNode treeNode = new TreeNode(certifyingKey); + children.add(treeNode); + } + } + + if (!children.isEmpty()) { + result.setChildren(children.toArray(TreeNode[]::new)); + children.forEach(child -> { + child.setParent(result); + PGPPublicKey certifyingKey = (PGPPublicKey) child.getValue(); + visited.remove(certifyingKey); + expandChildren(child, certifyingKey, keyService, visited, remainingDepth - 1); + visited.add(certifyingKey); + }); + } + } } - return children; } @Override public AuthenticationInfo getUsernamePassword(final String location, final AuthenticationInfo previousInfo) { final AuthenticationInfo[] result = new AuthenticationInfo[1]; if (!suppressAuthentication() && !isHeadless()) { - PlatformUI.getWorkbench().getDisplay().syncExec(() -> { - Shell shell = ProvUI.getDefaultParentShell(); + getDisplay().syncExec(() -> { + Shell shell = shellProvider.getShell(); String message = null; if (previousInfo.saveResult()) message = NLS.bind(ProvUIMessages.ProvUIMessages_SavedNotAccepted_EnterFor_0, location); @@ -246,18 +361,131 @@ public class ValidationDialogServiceUI extends UIServices { super.showInformationMessage(title, text, linkText); return; } - PlatformUI.getWorkbench().getDisplay().syncExec(() -> { - MessageDialog dialog = new MessageDialogWithLink(ProvUI.getDefaultParentShell(), title, null, text, - MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL }, 0, linkText); + getDisplay().syncExec(() -> { + MessageDialog dialog = new MessageDialogWithLink(shellProvider.getShell(), title, null, text, + MessageDialog.INFORMATION, new String[] { IDialogConstants.OK_LABEL }, 0, linkText, linkHandler); dialog.open(); }); } + /** + * Returns a handler (used by + * {@link #showInformationMessage(String, String, String)}) to open a link in an + * external browser. + * + * @return a handler to open a link in an external browser. + * + * @since 2.7.400 + */ + public Consumer<String> getLinkHandler() { + return linkHandler; + } + + /** + * Sets the handler used by + * {@link #showInformationMessage(String, String, String)} to open a link in an + * external browser. + * + * @param linkHandler the handler for opening links in an external browser. + * + * @since 2.7.400 + */ + public void setLinkHandler(Consumer<String> linkHandler) { + this.linkHandler = linkHandler; + } + + /** + * Returns a shell that is appropriate to use as the parent for a modal dialog + * about to be opened. + * + * @return a shell that is appropriate to use as the parent for a modal dialog + * about to be opened. + * + * @see ProvUI#getDefaultParentShell() + * + * @since 2.7.400 + */ + public IShellProvider getShellProvider() { + return shellProvider; + } + + /** + * Set the provider that yields a shell that is appropriate to use as the parent + * for a modal dialog about to be opened. + * + * @param shellProvider the provider of a shell that is appropriate to use as + * the parent for a modal dialog about to be opened. + */ + public void setShellProvider(IShellProvider shellProvider) { + this.shellProvider = shellProvider; + } + + /** + * Returns the display of the workbench or the display set by + * {@link #setDisplay(Display)} in a non-workbench application. + * + * @since 2.7.400 + */ + public Display getDisplay() { + if (display == null && PlatformUI.isWorkbenchRunning()) { + display = PlatformUI.getWorkbench().getDisplay(); + } + return display; + } + + /** + * Can be called once to set the display in a non-workbench application. + * + * @throws IllegalStateException if there is a workbench running or the display + * has already been set differently. + * + * @since 2.7.400 + */ + public void setDisplay(Display display) { + if (this.display != null && this.display != display) { + throw new IllegalStateException("Cannot change the display"); //$NON-NLS-1$ + } + this.display = display; + } + private boolean isHeadless() { // If there is no UI available and we are still the IServiceUI, // assume that the operation should proceed. See // https://bugs.eclipse.org/bugs/show_bug.cgi?id=291049 - return !PlatformUI.isWorkbenchRunning(); + // + // But we shouldn't just assume that no workbench mean no display and no head. + // It should be possible to reuse this class in an application without a + // workbench, e.g., the Eclipse Installer. + return getDisplay() == null; + } + + private static class ExtendedTreeNode extends TreeNode implements IAdaptable { + private final Set<IArtifactKey> artifacts; + + public ExtendedTreeNode(Object value) { + super(value); + artifacts = null; + } + + public ExtendedTreeNode(Object value, Set<IArtifactKey> artifacts) { + super(value); + this.artifacts = artifacts; + } + + @Override + public <T> T getAdapter(Class<T> adapter) { + if (adapter.isInstance(this)) { + return adapter.cast(this); + } + if (adapter.isInstance(getValue())) { + return adapter.cast(value); + } + if (adapter == IArtifactKey[].class && artifacts != null) { + return adapter.cast(artifacts.toArray(IArtifactKey[]::new)); + } + + return null; + } } } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PGPPublicKeyViewDialog.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PGPPublicKeyViewDialog.java new file mode 100644 index 000000000..a6cbf3be1 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PGPPublicKeyViewDialog.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.ui.dialogs; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.List; +import org.bouncycastle.bcpg.*; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.*; + +/** + * Presents information about a key in a format similar to what key servers + * display. + * + * @since 1.2.4 + */ +public class PGPPublicKeyViewDialog extends TitleAreaDialog { + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); //$NON-NLS-1$ + + final private PGPPublicKey originalKey; + + final private PGPPublicKeyService keyService; + + private StyledText styledText; + + public PGPPublicKeyViewDialog(Shell parentShell, PGPPublicKey key, PGPPublicKeyService keyService) { + super(parentShell); + this.originalKey = key; + this.keyService = keyService; + DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); //$NON-NLS-1$ + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(ProvUIMessages.PGPPublicKeyViewDialog_Title); + if (keyService != null) { + computeVerifiedCertifications(newShell); + } + } + + @Override + protected void setShellStyle(int newShellStyle) { + super.setShellStyle(newShellStyle | SWT.RESIZE | SWT.DIALOG_TRIM); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite composite = (Composite) super.createDialogArea(parent); + GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + composite.setLayoutData(data); + + styledText = new StyledText(composite, SWT.READ_ONLY | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); + styledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + // Create a sightly smaller text (mono-space) font. + FontData[] fontData = JFaceResources.getTextFont().getFontData(); + for (FontData fontDataElement : fontData) { + fontDataElement.setHeight(fontDataElement.getHeight() - 1); + } + Font font = new Font(styledText.getDisplay(), fontData); + styledText.setFont(font); + styledText.addDisposeListener(e -> font.dispose()); + + GC gc = new GC(styledText); + gc.setFont(font); + data.widthHint = convertWidthInCharsToPixels(gc.getFontMetrics(), 110); + gc.dispose(); + + update(originalKey, Set.of()); + return composite; + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CLOSE_LABEL, true).setFocus(); + } + + @SuppressWarnings("nls") + protected void update(PGPPublicKey key, Set<PGPPublicKey> verifiedCertifications) { + StyledString content = new StyledString(); + String fingerprint = PGPPublicKeyService.toHexFingerprint(key); + + PublicKeyPacket publicKeyPacket = key.getPublicKeyPacket(); + publicKeyPacket.getAlgorithm(); + content.append(" "); + content.append(publicKeyPacket instanceof PublicSubkeyPacket ? "sub" : "pub", StyledString.QUALIFIER_STYLER); + content.append(" "); + + int algorithm = publicKeyPacket.getAlgorithm(); + switch (algorithm) { + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_ENCRYPT: + case PublicKeyAlgorithmTags.RSA_SIGN: { + content.append("rsa"); + break; + } + case PublicKeyAlgorithmTags.DSA: { + content.append("dsa"); + break; + } + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: { + content.append("elgamal"); + break; + } + default: { + content.append("["); + content.append(Integer.toString(algorithm)); + content.append("]"); + break; + } + + } + int bitStrength = key.getBitStrength(); + content.append(Integer.toString(bitStrength)); + content.append("/"); + content.append(fingerprint); + + content.append(" "); + content.append(DATE_FORMAT.format(key.getCreationTime())); + + content.append(" "); + content.append("\n"); + + List<String> users = new ArrayList<>(); + key.getUserIDs().forEachRemaining(users::add); + if (!users.isEmpty()) { + for (String user : users) { + content.append(" "); + content.append("uid", StyledString.QUALIFIER_STYLER); + content.append(" "); + content.append(user, StyledString.COUNTER_STYLER); + content.append("\n"); + } + } + + Long subKeyOf = null; + + for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long keyID = signature.getKeyID(); + + if (signature.getSignatureType() == PGPSignature.SUBKEY_BINDING) { + subKeyOf = keyID; + } + + content.append(" "); + content.append("sig", StyledString.QUALIFIER_STYLER); + content.append(" "); + content.append(PGPPublicKeyService.toHex(keyID)); + content.append(" "); + Date creationTime = signature.getCreationTime(); + String formattedCreationTime = DATE_FORMAT.format(creationTime); + content.append(formattedCreationTime); + long signatureExpirationTime = signature.getHashedSubPackets().getSignatureExpirationTime(); + content.append(" "); + content.append(signatureExpirationTime == 0 ? formattedCreationTime.replaceAll(".", "_") + : DATE_FORMAT.format(new Date(creationTime.getTime() + 1000 * signatureExpirationTime))); + + content.append(" "); + Optional<PGPPublicKey> resolvedKey = verifiedCertifications.stream().filter(k -> k.getKeyID() == keyID) + .findFirst(); + + long keyExpirationTime = signature.getHashedSubPackets().getKeyExpirationTime(); + content.append(keyExpirationTime == 0 || resolvedKey == null || !resolvedKey.isPresent() + ? formattedCreationTime.replaceAll(".", "_") + : DATE_FORMAT.format( + new Date(resolvedKey.get().getCreationTime().getTime() + 1000 * keyExpirationTime))); + + if (resolvedKey != null && resolvedKey.isPresent()) { + content.append(" "); + content.append(getLabel(resolvedKey.get()), StyledString.COUNTER_STYLER); + } + + content.append("\n"); + } + + styledText.setText(content.getString()); + styledText.setStyleRanges(content.getStyleRanges()); + + List<String> title = new ArrayList<>(); + if (subKeyOf != null) { + long keyID = subKeyOf; + verifiedCertifications.stream().filter(k -> k.getKeyID() == keyID).findFirst() + .ifPresentOrElse(k -> title.add(getLabel(k)), () -> title.add(PGPPublicKeyService.toHex(keyID))); + } + title.add((subKeyOf == null ? "" : "sub ") + (users.isEmpty() ? fingerprint : users.get(0))); + + setTitle(String.join("\n", title)); + } + + private String getLabel(PGPPublicKey key) { + Iterator<String> userIDs = key.getUserIDs(); + if (userIDs.hasNext()) { + return userIDs.next(); + + } + return PGPPublicKeyService.toHexFingerprint(key); + } + + private void computeVerifiedCertifications(Shell shell) { + Display display = shell.getDisplay(); + new Job(PGPPublicKeyViewDialog.class.getName()) { + { + setSystem(true); + setPriority(Job.SHORT); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + synchronized (keyService) { + PGPPublicKey enhancedKey = keyService.addKey(originalKey); + Set<PGPPublicKey> verifiedCertifications = keyService.getVerifiedCertifications(originalKey); + display.asyncExec(() -> { + if (!shell.isDisposed()) { + update(enhancedKey, verifiedCertifications); + } + }); + } + return Status.OK_STATUS; + } + }.schedule(); + } +} diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java index 71479559e..47901c652 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java @@ -204,7 +204,7 @@ public abstract class ProvisioningOperationWizard extends Wizard { if (!operation.hasResolved()) { operation.resolveModal(monitor); } - if (operation.hasResolved()) { + if (operation.getProfileChangeRequest() != null) { this.localJRECheckPlan = ProvUI.toCompabilityWithCurrentJREProvisioningPlan(operation, null); if (!compatibleWithCurrentEE()) { couldNotResolveStatus = localJRECheckPlan.getStatus(); @@ -215,6 +215,10 @@ public abstract class ProvisioningOperationWizard extends Wizard { return false; } } + if (localJRECheckPlan == null) { + return true; + } + IStatus currentEEPlanStatus = localJRECheckPlan.getStatus(); if (currentEEPlanStatus.getSeverity() != IStatus.ERROR) { return true; @@ -337,7 +341,8 @@ public abstract class ProvisioningOperationWizard extends Wizard { try { runnableContext.run(true, true, monitor -> { operation.resolveModal(monitor); - if (getPolicy().getCheckAgainstCurrentExecutionEnvironment()) { + if (operation.getProfileChangeRequest() != null + && getPolicy().getCheckAgainstCurrentExecutionEnvironment()) { this.localJRECheckPlan = ProvUI.toCompabilityWithCurrentJREProvisioningPlan(operation, monitor); if (!compatibleWithCurrentEE()) { couldNotResolveStatus = localJRECheckPlan.getStatus(); diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java index 4c4fc386e..2d6cc8237 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java @@ -17,111 +17,181 @@ package org.eclipse.equinox.internal.p2.ui.dialogs; import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter; import java.io.*; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Iterator; +import java.security.cert.*; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.List; import java.util.function.Function; +import java.util.stream.Collectors; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPPublicKey; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.equinox.internal.p2.ui.ProvUIActivator; -import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.ui.*; import org.eclipse.equinox.internal.p2.ui.viewers.CertificateLabelProvider; import org.eclipse.equinox.internal.provisional.security.ui.X500PrincipalHelper; -import org.eclipse.equinox.internal.provisional.security.ui.X509CertificateViewDialog; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.jface.dialogs.*; import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.Policy; import org.eclipse.jface.viewers.*; +import org.eclipse.jface.widgets.LabelFactory; +import org.eclipse.jface.widgets.WidgetFactory; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.dnd.*; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.SelectionDialog; /** * A dialog that displays a certificate chain and asks the user if they trust - * the certificate providers. + * the certificate providers. It also supports prompting about unsigned content. */ public class TrustCertificateDialog extends SelectionDialog { + private static final String EXPORT_FILTER_PATH = "exportFilterPath"; //$NON-NLS-1$ - private Object inputElement; - private IStructuredContentProvider contentProvider; + private CheckboxTableViewer certifcateViewer; - private static final int SIZING_SELECTION_WIDGET_HEIGHT = 250; - private static final int SIZING_SELECTION_WIDGET_WIDTH = 300; + private TreeViewer certificateChainViewer; - CheckboxTableViewer listViewer; + private CheckboxTableViewer artifactViewer; - private TreeViewer certificateChainViewer; - protected TreeNode parentElement; - protected Object selectedCertificate; private Button detailsButton; + private Button exportButton; + + private boolean rememberSelectedSigners = true; + + private boolean trustAlways; + + private final Map<TreeNode, List<IArtifactKey>> artifactMap = new LinkedHashMap<>(); + public TrustCertificateDialog(Shell parentShell, Object input) { super(parentShell); - inputElement = input; - this.contentProvider = new TreeNodeContentProvider(); + + setShellStyle(SWT.DIALOG_TRIM | SWT.MODELESS | SWT.RESIZE | SWT.MAX | getDefaultOrientation()); + + if (input instanceof TreeNode[]) { + for (TreeNode node : (TreeNode[]) input) { + IArtifactKey[] associatedArtifacts = null; + if (node instanceof IAdaptable) { + associatedArtifacts = ((IAdaptable) node).getAdapter(IArtifactKey[].class); + } + artifactMap.put(node, associatedArtifacts == null ? List.of() : Arrays.asList(associatedArtifacts)); + } + } + setTitle(ProvUIMessages.TrustCertificateDialog_Title); - setMessage(ProvUIMessages.TrustCertificateDialog_Message); - setShellStyle(SWT.DIALOG_TRIM | SWT.MODELESS | SWT.RESIZE | getDefaultOrientation()); - } - private static class PGPOrX509ColumnLabelProvider extends ColumnLabelProvider { - private Function<PGPPublicKey, String> pgpMap; - private Function<X509Certificate, String> x509map; + boolean unsignedContent = artifactMap.keySet().stream().map(TreeNode::getValue).anyMatch(Objects::isNull); + boolean pgpContent = containsInstance(input, PGPPublicKey.class); + boolean certifcateContent = containsInstance(input, Certificate.class); - public PGPOrX509ColumnLabelProvider(Function<PGPPublicKey, String> pgpMap, - Function<X509Certificate, String> x509map) { - this.pgpMap = pgpMap; - this.x509map = x509map; + List<String> messages = new ArrayList<>(); + if (certifcateContent || pgpContent) { + messages.add(ProvUIMessages.TrustCertificateDialog_Message); + } + if (unsignedContent) { + messages.add(ProvUIMessages.TrustCertificateDialog_MessageUnsigned); + } + if (certifcateContent || pgpContent) { + messages.add(ProvUIMessages.TrustCertificateDialog_MessageNameWarning); + } + if (pgpContent) { + messages.add(ProvUIMessages.TrustCertificateDialog_MessagePGP); } + setMessage(String.join(" ", messages)); //$NON-NLS-1$ - @Override - public String getText(Object element) { - if (element instanceof TreeNode) { - element = ((TreeNode) element).getValue(); - } - if (element instanceof PGPPublicKey) { - return pgpMap.apply((PGPPublicKey) element); - } - if (element instanceof X509Certificate) { - return x509map.apply((X509Certificate) element); - } - return super.getText(element); + if (PlatformUI.isWorkbenchRunning()) { + PlatformUI.getWorkbench().getHelpSystem().setHelp(parentShell, IProvHelpContextIds.TRUST_DIALOG); + } + } + + public boolean isRememberSelectedSigners() { + return rememberSelectedSigners; + } + + public boolean isTrustAlways() { + return trustAlways; + } + + @Override + protected Label createMessageArea(Composite composite) { + // Ensure that the message supports wrapping for a long text message. + GridData data = new GridData(SWT.FILL, SWT.FILL, true, false); + data.widthHint = convertWidthInCharsToPixels(120); + LabelFactory factory = WidgetFactory.label(SWT.WRAP).font(composite.getFont()).layoutData(data); + if (getMessage() != null) { + factory.text(getMessage()); } + return factory.create(composite); } @Override protected Control createDialogArea(Composite parent) { - Composite composite = createUpperDialogArea(parent); - certificateChainViewer = new TreeViewer(composite, SWT.BORDER); - GridLayout layout = new GridLayout(); - certificateChainViewer.getTree().setLayout(layout); - GridData data = new GridData(GridData.FILL_BOTH); - data.grabExcessHorizontalSpace = true; - certificateChainViewer.getTree().setLayoutData(data); - certificateChainViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS); - certificateChainViewer.setContentProvider(new TreeNodeContentProvider()); - certificateChainViewer.setLabelProvider(new CertificateLabelProvider()); - certificateChainViewer.addSelectionChangedListener(getChainSelectionListener()); - if (inputElement instanceof Object[]) { - ISelection selection = null; - Object[] nodes = (Object[]) inputElement; - if (nodes.length > 0) { - selection = new StructuredSelection(nodes[0]); - certificateChainViewer.setInput(new TreeNode[] { (TreeNode) nodes[0] }); - selectedCertificate = nodes[0]; - } - listViewer.setSelection(selection); + Composite mainComposite = (Composite) super.createDialogArea(parent); + Dialog.applyDialogFont(mainComposite); + initializeDialogUnits(mainComposite); + + createMessageArea(mainComposite); + + SashForm sashForm = new SashForm(mainComposite, SWT.VERTICAL); + sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + createCertificateViewerArea(createSashFormArea(sashForm)); + + // Create this area only if we have keys or certificates. + boolean containsCertificates = containsInstance(artifactMap.keySet(), PGPPublicKey.class) + || containsInstance(artifactMap.keySet(), Certificate.class); + if (containsCertificates) { + createCertficateChainViewerArea(createSashFormArea(sashForm)); } - listViewer.addDoubleClickListener(getDoubleClickListener()); - listViewer.addSelectionChangedListener(getParentSelectionListener()); - createButtons(composite); - detailsButton.setEnabled(selectedCertificate instanceof X509Certificate); - return composite; + + // Sort the set of all artifacts and create the lower area only if there are + // artifacts. + Comparator<Object> comparator = Policy.getComparator(); + Set<IArtifactKey> artifacts = artifactMap.values().stream().flatMap(Collection::stream) + .collect(Collectors.toCollection(() -> new TreeSet<>((a1, a2) -> { + int result = comparator.compare(a1.getId(), a2.getId()); + if (result == 0) { + result = a1.getVersion().compareTo(a2.getVersion()); + if (result == 0) { + result = a1.getClassifier().compareTo(a2.getClassifier()); + } + } + return result; + }))); + if (!artifacts.isEmpty()) { + crreateArtifactViewerArea(createSashFormArea(sashForm), artifacts); + } + + // Set weights based on the children's preferred size. + Control[] children = sashForm.getChildren(); + int[] weights = new int[children.length]; + for (int i = 0; i < children.length; ++i) { + weights[i] = children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, false).y; + } + sashForm.setWeights(weights); + + if (!getInitialElementSelections().isEmpty()) { + checkInitialSelections(); + } + + if (!artifactMap.isEmpty()) { + certifcateViewer.setSelection(new StructuredSelection(artifactMap.keySet().iterator().next())); + } + + return mainComposite; } @Override @@ -129,26 +199,21 @@ public class TrustCertificateDialog extends SelectionDialog { createButton(parent, IDialogConstants.OK_ID, ProvUIMessages.TrustCertificateDialog_AcceptSelectedButtonLabel, true); createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); - super.getOkButton().setEnabled(false); + updateOkButton(); } private void createButtons(Composite composite) { Composite buttonComposite = new Composite(composite, SWT.NONE); buttonComposite.setLayout(new RowLayout()); - // Details button to view certificate chain + detailsButton = new Button(buttonComposite, SWT.NONE); detailsButton.setText(ProvUIMessages.TrustCertificateDialog_Details); detailsButton.addSelectionListener(new SelectionListener() { @Override public void widgetDefaultSelected(SelectionEvent e) { - Object o = selectedCertificate; - if (selectedCertificate instanceof TreeNode) { - o = ((TreeNode) selectedCertificate).getValue(); - } - if (o instanceof X509Certificate) { - X509Certificate cert = (X509Certificate) o; - X509CertificateViewDialog certificateDialog = new X509CertificateViewDialog(getShell(), cert); - certificateDialog.open(); + X509Certificate cert = getInstance(certificateChainViewer.getSelection(), X509Certificate.class); + if (cert != null) { + CertificateLabelProvider.openDialog(getShell(), cert); } } @@ -158,46 +223,48 @@ public class TrustCertificateDialog extends SelectionDialog { } }); - Button exportButton = new Button(buttonComposite, SWT.NONE); + exportButton = new Button(buttonComposite, SWT.NONE); exportButton.setText(ProvUIMessages.TrustCertificateDialog_Export); exportButton.addSelectionListener(new SelectionListener() { @Override public void widgetDefaultSelected(SelectionEvent e) { - Object o = selectedCertificate; - if (selectedCertificate instanceof TreeNode) { - o = ((TreeNode) selectedCertificate).getValue(); - } - FileDialog destination = new FileDialog(detailsButton.getShell(), SWT.SAVE); - destination.setText(ProvUIMessages.TrustCertificateDialog_Export); - if (o instanceof X509Certificate) { - X509Certificate cert = (X509Certificate) o; - destination.setFilterExtensions(new String[] { "*.der" }); //$NON-NLS-1$ - destination.setFileName(cert.getSerialNumber().toString() + ".der"); //$NON-NLS-1$ - String path = destination.open(); - if (path == null) { - return; - } - File destinationFile = new File(path); - try (FileOutputStream output = new FileOutputStream(destinationFile)) { - output.write(cert.getEncoded()); - } catch (IOException | CertificateEncodingException ex) { - ProvUIActivator.getDefault().getLog() - .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); - } - } else if (o instanceof PGPPublicKey) { - PGPPublicKey key = (PGPPublicKey) o; - destination.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ - destination.setFileName(Long.toHexString(key.getKeyID()) + ".asc"); //$NON-NLS-1$ - String path = destination.open(); - if (path == null) { - return; - } - File destinationFile = new File(path); - try (OutputStream output = new ArmoredOutputStream(new FileOutputStream(destinationFile))) { - output.write(key.getEncoded()); - } catch (IOException ex) { - ProvUIActivator.getDefault().getLog() - .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + ISelection selection = certificateChainViewer.getSelection(); + X509Certificate cert = getInstance(selection, X509Certificate.class); + PGPPublicKey key = getInstance(selection, PGPPublicKey.class); + if (cert != null || key != null) { + FileDialog destination = new FileDialog(exportButton.getShell(), SWT.SAVE); + destination.setFilterPath(getFilterPath(EXPORT_FILTER_PATH)); + destination.setText(ProvUIMessages.TrustCertificateDialog_Export); + if (cert != null) { + destination.setFilterExtensions(new String[] { "*.der" }); //$NON-NLS-1$ + destination.setFileName(cert.getSerialNumber().toString() + ".der"); //$NON-NLS-1$ + String path = destination.open(); + setFilterPath(EXPORT_FILTER_PATH, destination.getFilterPath()); + if (path == null) { + return; + } + File destinationFile = new File(path); + try (FileOutputStream output = new FileOutputStream(destinationFile)) { + output.write(cert.getEncoded()); + } catch (IOException | CertificateEncodingException ex) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } + } else { + destination.setFilterExtensions(new String[] { "*.asc" }); //$NON-NLS-1$ + destination.setFileName(userFriendlyFingerPrint(key) + ".asc"); //$NON-NLS-1$ + String path = destination.open(); + setFilterPath(EXPORT_FILTER_PATH, destination.getFilterPath()); + if (path == null) { + return; + } + File destinationFile = new File(path); + try (OutputStream output = new ArmoredOutputStream(new FileOutputStream(destinationFile))) { + key.encode(output); + } catch (IOException ex) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, ex.getMessage(), ex)); + } } } } @@ -206,56 +273,277 @@ public class TrustCertificateDialog extends SelectionDialog { public void widgetSelected(SelectionEvent e) { widgetDefaultSelected(e); } - }); } - private Composite createUpperDialogArea(Composite parent) { - Composite composite = (Composite) super.createDialogArea(parent); - initializeDialogUnits(composite); - createMessageArea(composite); + private Composite createSashFormArea(SashForm sashForm) { + Composite composite = new Composite(sashForm, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); + layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + composite.setLayout(layout); + return composite; + } + + private void createCertificateViewerArea(Composite composite) { + + TableColumnLayout tableColumnLayout = new TableColumnLayout(true); + Composite tableComposite = WidgetFactory.composite(SWT.NONE) + .layoutData(new GridData(SWT.FILL, SWT.FILL, true, true)).layout(tableColumnLayout).create(composite); + Table table = WidgetFactory + .table(SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION | SWT.CHECK) + .headerVisible(true).linesVisible(true).font(composite.getFont()).create(tableComposite); + certifcateViewer = new CheckboxTableViewer(table); + certifcateViewer.setContentProvider(new TreeNodeContentProvider()); - listViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER); GridData data = new GridData(GridData.FILL_BOTH); - data.heightHint = SIZING_SELECTION_WIDGET_HEIGHT; - data.widthHint = SIZING_SELECTION_WIDGET_WIDTH; - listViewer.getTable().setLayoutData(data); - - listViewer.setContentProvider(contentProvider); - TableViewerColumn typeColumn = new TableViewerColumn(listViewer, SWT.NONE); - typeColumn.getColumn().setWidth(80); - typeColumn.getColumn().setText(ProvUIMessages.TrustCertificateDialog_ObjectType); - typeColumn.setLabelProvider(new PGPOrX509ColumnLabelProvider(key -> "PGP", cert -> "x509")); //$NON-NLS-1$ //$NON-NLS-2$ - TableViewerColumn idColumn = new TableViewerColumn(listViewer, SWT.NONE); - idColumn.getColumn().setWidth(200); - idColumn.getColumn().setText(ProvUIMessages.TrustCertificateDialog_Id); - idColumn.setLabelProvider(new PGPOrX509ColumnLabelProvider(key -> Long.toHexString(key.getKeyID()), - cert -> cert.getSerialNumber().toString())); - TableViewerColumn signerColumn = new TableViewerColumn(listViewer, SWT.NONE); - signerColumn.getColumn().setText(ProvUIMessages.TrustCertificateDialog_Name); - signerColumn.getColumn().setWidth(400); - signerColumn.setLabelProvider(new PGPOrX509ColumnLabelProvider(pgp -> { - java.util.List<String> users = new ArrayList<>(); - pgp.getUserIDs().forEachRemaining(users::add); - return String.join(",", users); //$NON-NLS-1$ - }, x509 -> { - X500PrincipalHelper principalHelper = new X500PrincipalHelper(x509.getSubjectX500Principal()); - return principalHelper.getCN() + "; " + principalHelper.getOU() + "; " //$NON-NLS-1$ //$NON-NLS-2$ - + principalHelper.getO(); - })); - listViewer.getTable().setHeaderVisible(true); + data.heightHint = convertHeightInCharsToPixels(Math.min(artifactMap.keySet().size() + 1, 6)) * 3 / 2; + data.widthHint = convertWidthInCharsToPixels(120); + tableComposite.setLayoutData(data); + + // This column is packed later. + TableViewerColumn typeColumn = createColumn(certifcateViewer, ProvUIMessages.TrustCertificateDialog_ObjectType, + new PGPOrX509ColumnLabelProvider(key -> "PGP", cert -> "x509", //$NON-NLS-1$ //$NON-NLS-2$ + ProvUIMessages.TrustCertificateDialog_Unsigned), + tableColumnLayout, 1); + + createColumn(certifcateViewer, ProvUIMessages.TrustCertificateDialog_Id, + new PGPOrX509ColumnLabelProvider(TrustCertificateDialog::userFriendlyFingerPrint, + cert -> cert.getSerialNumber().toString(), ProvUIMessages.TrustCertificateDialog_NotApplicable), + tableColumnLayout, 10); + + createColumn(certifcateViewer, ProvUIMessages.TrustCertificateDialog_Name, + new PGPOrX509ColumnLabelProvider(pgp -> { + java.util.List<String> users = new ArrayList<>(); + pgp.getUserIDs().forEachRemaining(users::add); + return String.join(", ", users); //$NON-NLS-1$ + }, x509 -> { + X500PrincipalHelper principalHelper = new X500PrincipalHelper(x509.getSubjectX500Principal()); + return principalHelper.getCN() + "; " + principalHelper.getOU() + "; " //$NON-NLS-1$ //$NON-NLS-2$ + + principalHelper.getO(); + }, ProvUIMessages.TrustCertificateDialog_Unknown), tableColumnLayout, 15); + + createColumn(certifcateViewer, ProvUIMessages.TrustCertificateDialog_dates, + new PGPOrX509ColumnLabelProvider(pgp -> { + if (pgp.getCreationTime().after(Date.from(Instant.now()))) { + return NLS.bind(ProvUIMessages.TrustCertificateDialog_NotYetValidStartDate, + pgp.getCreationTime()); + } + long validSeconds = pgp.getValidSeconds(); + if (validSeconds == 0) { + return ProvUIMessages.TrustCertificateDialog_valid; + } + Instant expires = pgp.getCreationTime().toInstant().plus(validSeconds, ChronoUnit.SECONDS); + return expires.isBefore(Instant.now()) + ? NLS.bind(ProvUIMessages.TrustCertificateDialog_expiredSince, expires) + : NLS.bind(ProvUIMessages.TrustCertificateDialog_validExpires, expires); + }, x509 -> { + try { + x509.checkValidity(); + return ProvUIMessages.TrustCertificateDialog_valid; + } catch (CertificateExpiredException expired) { + return ProvUIMessages.TrustCertificateDialog_expired; + } catch (CertificateNotYetValidException notYetValid) { + return ProvUIMessages.TrustCertificateDialog_notYetValid; + } + }, ProvUIMessages.TrustCertificateDialog_NotApplicable), tableColumnLayout, 10); + + createMenu(certifcateViewer); addSelectionButtons(composite); - listViewer.setInput(inputElement); + certifcateViewer.addDoubleClickListener(e -> { + StructuredSelection selection = (StructuredSelection) e.getSelection(); + X509Certificate cert = getInstance(selection, X509Certificate.class); + if (cert != null) { + // create and open dialog for certificate chain + CertificateLabelProvider.openDialog(getShell(), cert); + } + }); - if (!getInitialElementSelections().isEmpty()) { - checkInitialSelections(); + certifcateViewer.addSelectionChangedListener(e -> { + if (certificateChainViewer != null) { + TreeNode treeNode = getInstance(e.getSelection(), TreeNode.class); + if (treeNode != null) { + certificateChainViewer.setInput(new TreeNode[] { treeNode }); + certificateChainViewer.setSelection(new StructuredSelection(treeNode)); + } else { + certificateChainViewer.setInput(new TreeNode[] {}); + } + } + + updateOkButton(); + }); + + certifcateViewer.setInput(artifactMap.keySet().toArray(TreeNode[]::new)); + + typeColumn.getColumn().pack(); + } + + private void createCertficateChainViewerArea(Composite composite) { + certificateChainViewer = new TreeViewer(composite, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); + GridData data = new GridData(GridData.FILL_BOTH); + int treeSize = artifactMap.keySet().stream().map(TrustCertificateDialog::computeTreeSize) + .max(Integer::compareTo).orElse(0); + data.heightHint = convertHeightInCharsToPixels(Math.min(treeSize, 5)); + data.widthHint = convertWidthInCharsToPixels(120); + certificateChainViewer.getTree().setLayoutData(data); + + certificateChainViewer.setAutoExpandLevel(3); + certificateChainViewer.setContentProvider(new TreeNodeContentProvider()); + certificateChainViewer.setLabelProvider(new CertificateLabelProvider() { + @Override + public String getText(Object element) { + if (element instanceof TreeNode) { + Object o = ((TreeNode) element).getValue(); + if (o instanceof PGPPublicKey) { + PGPPublicKey key = (PGPPublicKey) o; + String userFriendlyFingerPrint = userFriendlyFingerPrint(key); + java.util.List<String> users = new ArrayList<>(); + key.getUserIDs().forEachRemaining(users::add); + String userIDs = String.join(", ", users); //$NON-NLS-1$ + if (!userIDs.isEmpty()) { + return userFriendlyFingerPrint + " [" + userIDs + "]"; //$NON-NLS-1$//$NON-NLS-2$ + } + return userFriendlyFingerPrint; + } else if (o == null) { + return ProvUIMessages.TrustCertificateDialog_Unsigned; + } + } + + return super.getText(element); + } + }); + + certificateChainViewer.addSelectionChangedListener(event -> { + ISelection selection = event.getSelection(); + boolean containsCertificate = containsInstance(selection, X509Certificate.class); + detailsButton.setEnabled(containsCertificate); + exportButton.setEnabled(containsCertificate || containsInstance(selection, PGPPublicKey.class)); + }); + + createMenu(certificateChainViewer); + + createButtons(composite); + } + + private void crreateArtifactViewerArea(Composite composite, Set<IArtifactKey> artifacts) { + TableColumnLayout tableColumnLayout = new TableColumnLayout(true); + Composite tableComposite = WidgetFactory.composite(SWT.NONE) + .layoutData(new GridData(SWT.FILL, SWT.FILL, true, true)).layout(tableColumnLayout).create(composite); + Table table = WidgetFactory.table(SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION) + .headerVisible(true).linesVisible(true).font(composite.getFont()).create(tableComposite); + artifactViewer = new CheckboxTableViewer(table); + artifactViewer.setContentProvider(ArrayContentProvider.getInstance()); + + GridData data = new GridData(GridData.FILL_BOTH); + data.heightHint = convertHeightInCharsToPixels(Math.min(artifacts.size() + 1, 10)) * 3 / 2; + data.widthHint = convertWidthInCharsToPixels(120); + tableComposite.setLayoutData(data); + + Font font = table.getFont(); + FontData[] fontDatas = font.getFontData(); + for (FontData fontData : fontDatas) { + fontData.setStyle(fontData.getStyle() | SWT.BOLD); } + Font boldFont = new Font(table.getDisplay(), fontDatas); + composite.addDisposeListener(e -> boldFont.dispose()); + + Function<IArtifactKey, Font> fontProvider = e -> { + for (Object object : certifcateViewer.getCheckedElements()) { + List<IArtifactKey> list = artifactMap.get(object); + if (list != null && list.contains(e)) { + return boldFont; + } + } + return null; + }; - Dialog.applyDialogFont(composite); + certifcateViewer.addCheckStateListener(e -> artifactViewer.refresh(true)); + + artifactViewer.addPostSelectionChangedListener(e -> { + if (table.isFocusControl()) { + List<?> selection = e.getStructuredSelection().toList(); + List<TreeNode> newSelection = new ArrayList<>(); + LOOP: for (Map.Entry<TreeNode, List<IArtifactKey>> entry : artifactMap.entrySet()) { + List<IArtifactKey> value = entry.getValue(); + if (value != null) { + for (IArtifactKey key : value) { + if (selection.contains(key)) { + newSelection.add(entry.getKey()); + continue LOOP; + } + } + } + } - return composite; + certifcateViewer.setSelection(new StructuredSelection(newSelection), true); + } + }); + + certifcateViewer.addPostSelectionChangedListener(e -> { + if (!table.isFocusControl()) { + Set<IArtifactKey> associatedArtifacts = new LinkedHashSet<>(); + for (Object object : e.getStructuredSelection()) { + List<IArtifactKey> list = artifactMap.get(object); + if (list != null) { + associatedArtifacts.addAll(list); + } + } + + artifactViewer.setSelection(new StructuredSelection(associatedArtifacts.toArray()), true); + + // Reorder the artifacts so that the selected ones are first. + LinkedHashSet<IArtifactKey> newInput = new LinkedHashSet<>(artifacts); + newInput.retainAll(associatedArtifacts); + newInput.addAll(artifacts); + artifactViewer.setInput(newInput); + + artifactViewer.setSelection(new StructuredSelection(associatedArtifacts.toArray()), true); + } + }); + + TableViewerColumn classifierColumn = createColumn(artifactViewer, + ProvUIMessages.TrustCertificateDialog_Classifier, + new ArtifactLabelProvider(a -> a.getClassifier(), fontProvider), tableColumnLayout, 1); + createColumn(artifactViewer, ProvUIMessages.TrustCertificateDialog_ArtifactId, + new ArtifactLabelProvider(a -> a.getId(), fontProvider), tableColumnLayout, 10); + createColumn(artifactViewer, ProvUIMessages.TrustCertificateDialog_Version, + new ArtifactLabelProvider(a -> a.getVersion().toString(), fontProvider), tableColumnLayout, 10); + + artifactViewer.setInput(artifacts); + + classifierColumn.getColumn().pack(); + } + + private void createMenu(StructuredViewer viewer) { + Control control = viewer.getControl(); + Menu menu = new Menu(control); + control.setMenu(menu); + MenuItem item = new MenuItem(menu, SWT.PUSH); + item.setText(ProvUIMessages.TrustCertificateDialog_CopyFingerprint); + item.addSelectionListener(widgetSelectedAdapter(e -> { + PGPPublicKey key = getInstance(viewer.getSelection(), PGPPublicKey.class); + if (key != null) { + Clipboard clipboard = new Clipboard(getShell().getDisplay()); + clipboard.setContents(new Object[] { userFriendlyFingerPrint(key) }, + new Transfer[] { TextTransfer.getInstance() }); + clipboard.dispose(); + } + })); + viewer.addSelectionChangedListener( + e -> item.setEnabled(containsInstance(e.getSelection(), PGPPublicKey.class))); + } + + private TableViewerColumn createColumn(TableViewer tableViewer, String text, ColumnLabelProvider labelProvider, + TableColumnLayout tableColumnLayout, int columnWeight) { + TableViewerColumn column = new TableViewerColumn(tableViewer, SWT.NONE); + column.getColumn().setText(text); + column.setLabelProvider(labelProvider); + tableColumnLayout.setColumnData(column.getColumn(), new ColumnWeightData(columnWeight)); + return column; } /** @@ -265,7 +553,10 @@ public class TrustCertificateDialog extends SelectionDialog { private void checkInitialSelections() { Iterator<?> itemsToCheck = getInitialElementSelections().iterator(); while (itemsToCheck.hasNext()) { - listViewer.setChecked(itemsToCheck.next(), true); + certifcateViewer.setChecked(itemsToCheck.next(), true); + if (artifactViewer != null) { + artifactViewer.refresh(true); + } } } @@ -275,93 +566,239 @@ public class TrustCertificateDialog extends SelectionDialog { * @param composite org.eclipse.swt.widgets.Composite */ private void addSelectionButtons(Composite composite) { - Composite buttonComposite = new Composite(composite, SWT.NONE); - GridLayout layout = new GridLayout(); - layout.numColumns = 0; - layout.marginWidth = 0; - layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); - buttonComposite.setLayout(layout); - buttonComposite.setLayoutData(new GridData(SWT.END, SWT.TOP, true, false)); + int horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + + Composite buttonArea = new Composite(composite, SWT.NONE); + GridLayout buttonAreaLayout = new GridLayout(); + buttonAreaLayout.numColumns = 2; + buttonAreaLayout.marginWidth = 0; + buttonAreaLayout.horizontalSpacing = horizontalSpacing; + buttonArea.setLayout(buttonAreaLayout); + buttonArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + + Composite leftButtonArea = new Composite(buttonArea, SWT.NONE); + GridLayout leftButtonAreaLayout = new GridLayout(); + leftButtonAreaLayout.numColumns = 0; + leftButtonAreaLayout.marginWidth = 0; + leftButtonAreaLayout.horizontalSpacing = horizontalSpacing; + leftButtonArea.setLayout(leftButtonAreaLayout); + leftButtonArea.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false)); + + if (containsInstance(artifactMap.keySet(), PGPPublicKey.class) + || containsInstance(artifactMap.keySet(), Certificate.class)) { + Button rememberSelectionButton = createCheckButton(leftButtonArea, + ProvUIMessages.TrustCertificateDialog_RememberSigners); + rememberSelectionButton.setSelection(rememberSelectedSigners); + rememberSelectionButton.addSelectionListener(widgetSelectedAdapter(e -> { + rememberSelectedSigners = rememberSelectionButton.getSelection(); + })); + } - Button selectButton = createButton(buttonComposite, IDialogConstants.SELECT_ALL_ID, - ProvUIMessages.TrustCertificateDialog_SelectAll, false); + Button trustAlwaysButton = createCheckButton(leftButtonArea, ProvUIMessages.TrustCertificateDialog_AlwaysTrust); + trustAlwaysButton.addSelectionListener(widgetSelectedAdapter(e -> { + if (trustAlwaysButton.getSelection()) { + // Prompt the user to ensure they really understand what they've chosen, the + // risk, and where the preference is stored if they wish to change it in the + // future. Also ensure that the default button is no so that they must + // explicitly click the yes button, not just hit enter. + MessageDialog messageDialog = new MessageDialog(getShell(), + ProvUIMessages.TrustCertificateDialog_AlwaysTrustConfirmationTitle, null, + ProvUIMessages.TrustCertificateDialog_AlwaysTrustConfirmationMessage, MessageDialog.QUESTION, + new String[] { ProvUIMessages.TrustCertificateDialog_AlwaysTrustYes, + ProvUIMessages.TrustCertificateDialog_AlwaysTrustNo }, + 1) { + @Override + public Image getImage() { + return getWarningImage(); + } + }; + int result = messageDialog.open(); + if (result != Window.OK) { + // Restore the checkbox state. + trustAlwaysButton.setSelection(false); + } else { + certifcateViewer.setAllChecked(true); + if (artifactViewer != null) { + artifactViewer.refresh(true); + } + updateOkButton(); + } + } + trustAlways = trustAlwaysButton.getSelection(); + })); - SelectionListener listener = widgetSelectedAdapter(e -> { - listViewer.setAllChecked(true); - getOkButton().setEnabled(true); - }); - selectButton.addSelectionListener(listener); + Composite rightButtonArea = new Composite(buttonArea, SWT.NONE); + GridLayout rightButtonAreaLayout = new GridLayout(); + rightButtonAreaLayout.numColumns = 0; + rightButtonAreaLayout.marginWidth = 0; + rightButtonAreaLayout.horizontalSpacing = horizontalSpacing; + rightButtonArea.setLayout(rightButtonAreaLayout); + rightButtonArea.setLayoutData(new GridData(SWT.END, SWT.TOP, true, false)); - Button deselectButton = createButton(buttonComposite, IDialogConstants.DESELECT_ALL_ID, - ProvUIMessages.TrustCertificateDialog_DeselectAll, false); + Button selectButton = createButton(rightButtonArea, IDialogConstants.SELECT_ALL_ID, + ProvUIMessages.TrustCertificateDialog_SelectAll, false); + selectButton.addSelectionListener(widgetSelectedAdapter(e -> { + certifcateViewer.setAllChecked(true); + if (artifactViewer != null) { + artifactViewer.refresh(true); + } + updateOkButton(); + })); - listener = widgetSelectedAdapter(e -> { - listViewer.setAllChecked(false); - getOkButton().setEnabled(false); - }); - deselectButton.addSelectionListener(listener); + Button deselectButton = createButton(rightButtonArea, IDialogConstants.DESELECT_ALL_ID, + ProvUIMessages.TrustCertificateDialog_DeselectAll, false); + deselectButton.addSelectionListener(widgetSelectedAdapter(e -> { + certifcateViewer.setAllChecked(false); + if (artifactViewer != null) { + artifactViewer.refresh(true); + } + updateOkButton(); + })); } - private ISelectionChangedListener getChainSelectionListener() { - return event -> { - ISelection selection = event.getSelection(); - if (selection instanceof StructuredSelection) { - selectedCertificate = ((StructuredSelection) selection).getFirstElement(); - detailsButton.setEnabled(selectedCertificate instanceof X509Certificate); - } - }; + protected Button createCheckButton(Composite parent, String label) { + ((GridLayout) parent.getLayout()).numColumns++; + Button button = WidgetFactory.button(SWT.CHECK).text(label).font(JFaceResources.getDialogFont()).create(parent); + setButtonLayoutData(button); + return button; } - public TreeViewer getCertificateChainViewer() { - return certificateChainViewer; + private String getFilterPath(String key) { + IDialogSettings dialogSettings = DialogSettings + .getOrCreateSection(ProvUIActivator.getDefault().getDialogSettings(), getClass().getName()); + String filterPath = dialogSettings.get(key); + if (filterPath == null) { + filterPath = System.getProperty("user.home"); //$NON-NLS-1$ + } + return filterPath; } - private IDoubleClickListener getDoubleClickListener() { - return event -> { - StructuredSelection selection = (StructuredSelection) event.getSelection(); - Object selectedElement = selection.getFirstElement(); - if (selectedElement instanceof TreeNode) { - TreeNode treeNode = (TreeNode) selectedElement; - // create and open dialog for certificate chain - X509CertificateViewDialog certificateViewDialog = new X509CertificateViewDialog(getShell(), - (X509Certificate) treeNode.getValue()); - certificateViewDialog.open(); - } - }; + private void setFilterPath(String key, String filterPath) { + if (filterPath != null) { + IDialogSettings dialogSettings = DialogSettings + .getOrCreateSection(ProvUIActivator.getDefault().getDialogSettings(), getClass().getName()); + dialogSettings.put(key, filterPath); + } } - private ISelectionChangedListener getParentSelectionListener() { - return event -> { - ISelection selection = event.getSelection(); - if (selection instanceof StructuredSelection) { - TreeNode firstElement = (TreeNode) ((StructuredSelection) selection).getFirstElement(); - getCertificateChainViewer().setInput(new TreeNode[] { firstElement }); - getOkButton().setEnabled(listViewer.getChecked(firstElement)); - getCertificateChainViewer().refresh(); + private void updateOkButton() { + Button okButton = getOkButton(); + if (okButton != null) { + certifcateViewer.getCheckedElements(); + Object[] checkedElements = certifcateViewer.getCheckedElements(); + Set<IArtifactKey> artifacts = artifactMap.values().stream().flatMap(Collection::stream) + .collect(Collectors.toSet()); + if (artifacts.isEmpty()) { + okButton.setEnabled(checkedElements.length > 0); + } else { + for (Object checkElement : checkedElements) { + artifacts.removeAll(artifactMap.get(checkElement)); + } + okButton.setEnabled(artifacts.isEmpty()); } - }; + } } - /** - * The <code>ListSelectionDialog</code> implementation of this - * <code>Dialog</code> method builds a list of the selected elements for later - * retrieval by the client and closes this dialog. - */ @Override protected void okPressed() { - // Get the input children. - Object[] children = contentProvider.getElements(inputElement); + setResult(Arrays.asList(certifcateViewer.getCheckedElements())); + super.okPressed(); + } + + private static class PGPOrX509ColumnLabelProvider extends ColumnLabelProvider { + private Function<PGPPublicKey, String> pgpMap; + private Function<X509Certificate, String> x509map; + private String unsignedValue; + + public PGPOrX509ColumnLabelProvider(Function<PGPPublicKey, String> pgpMap, + Function<X509Certificate, String> x509map, String unsignedValue) { + this.pgpMap = pgpMap; + this.x509map = x509map; + this.unsignedValue = unsignedValue; + } + + @Override + public String getText(Object element) { + if (element instanceof TreeNode) { + element = ((TreeNode) element).getValue(); + } + if (element instanceof PGPPublicKey) { + return pgpMap.apply((PGPPublicKey) element); + } + if (element instanceof X509Certificate) { + return x509map.apply((X509Certificate) element); + } - // Build a list of selected children. + if (element == null) { + return unsignedValue; + } + return super.getText(element); + } + } + + private static class ArtifactLabelProvider extends ColumnLabelProvider { + private Function<IArtifactKey, String> labelProvider; + private Function<IArtifactKey, Font> fontProvider; + + public ArtifactLabelProvider(Function<IArtifactKey, String> labelProvider, + Function<IArtifactKey, Font> fontProvider) { + this.labelProvider = labelProvider; + this.fontProvider = fontProvider; + } + + @Override + public String getText(Object element) { + return labelProvider.apply((IArtifactKey) element); + } + + @Override + public Font getFont(Object element) { + return fontProvider.apply((IArtifactKey) element); + } + } + + private static int computeTreeSize(TreeNode node) { + int count = 1; + TreeNode[] children = node.getChildren(); if (children != null) { - ArrayList<Object> list = new ArrayList<>(); - for (Object element : children) { - if (listViewer.getChecked(element)) { - list.add(element); + for (TreeNode child : children) { + count += computeTreeSize(child); + } + } + return count; + } + + private static <T> T getInstance(Object element, Class<T> type) { + if (type.isInstance(element)) { + return type.cast(element); + } else if (element instanceof Iterable) { + for (Object object : ((Iterable<?>) element)) { + T instance = getInstance(object, type); + if (instance != null) { + return instance; + } + } + } else if (element instanceof TreeNode) { + return getInstance(((TreeNode) element).getValue(), type); + } else if (element instanceof TreeNode[]) { + for (TreeNode child : (TreeNode[]) element) { + T instance = getInstance(child, type); + if (instance != null) { + return instance; } } - setResult(list); } - super.okPressed(); + return null; + } + + private static boolean containsInstance(Object element, Class<?> type) { + return getInstance(element, type) != null; + } + + private static String userFriendlyFingerPrint(PGPPublicKey key) { + if (key == null) { + return null; + } + return PGPPublicKeyService.toHexFingerprint(key); } } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties index 561551af2..1d574dc40 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties @@ -12,6 +12,7 @@ # IBM Corporation - initial API and implementation ############################################################################### +PGPPublicKeyViewDialog_Title=PGP Public Key Properties ProfileModificationAction_InvalidSelections=Problem determining user request. Profile id: {0}, Selection count: {1} ProfileModificationWizardPage_DetailsLabel=Details ProfileSnapshots_Label=Installation History @@ -257,6 +258,10 @@ RepositoryElement_NotFound=This repository is currently not available. RepositoryTracker_DuplicateLocation=Duplicate location MetadataRepositoryElement_RepositoryLoadError=Error loading repository {0} IUViewQueryContext_NoCategorizedItemsDescription=You can uncheck the 'Group items by category' check box to see items without categories. +KeySigningInfoFactory_FingerprintItem=Fingerprint= +KeySigningInfoFactory_KeySignersSection=Key Signers: +KeySigningInfoFactory_PGPSigningType=PGP Public Key +KeySigningInfoFactory_UserIDItem=UserID= QueriedElementWrapper_NoCategorizedItemsExplanation=There are no categorized items QueriedElementWrapper_NoItemsExplanation=There are no items available QueriedElementWrapper_SiteNotFound=Could not find {0} @@ -276,13 +281,38 @@ RevertDialog_CancelButtonLabel=Cancel RollbackProfileElement_CurrentInstallation=Current Installation -TrustCertificateDialog_Details=\uD83D\uDD0D &Details... +TrustCertificateDialog_Details=\uD83D\uDD0D D&etails... TrustCertificateDialog_Export=\uD83D\uDCE5 E&xport... TrustCertificateDialog_Title=Trust TrustCertificateDialog_Message=Do you trust these signers? -TrustCertificateDialog_AcceptSelectedButtonLabel=&Trust selected +TrustCertificateDialog_MessageUnsigned=\u26A0\uFE0F\u00A0Do you trust unsigned content of unknown origin? +TrustCertificateDialog_MessageNameWarning=\u26A0\uFE0F\u00A0The displayed originator names are not necessarily a reliable certification of origin. +TrustCertificateDialog_MessagePGP=For PG keys, verification is typically achieved by querying the key\'s fingerprint against a trusted key server. +TrustCertificateDialog_AcceptSelectedButtonLabel=&Trust Selected +TrustCertificateDialog_AlwaysTrust=Always trust all content +TrustCertificateDialog_AlwaysTrustConfirmationMessage=Are you certain you wish to accept all content, including unsigned content of unknown origin, with no further confirmation now and for all future operations?\n\n\ +Use the 'Select All' button to trust all content just for this operation.\n\n\ +This preference choice will be stored on the 'Install/Update > Trust' preference page. +TrustCertificateDialog_AlwaysTrustConfirmationTitle=Always Trust Everything Confirmation +TrustCertificateDialog_AlwaysTrustNo=No, Prompt Me Instead +TrustCertificateDialog_AlwaysTrustYes=Yes, I Accept the Risk +TrustCertificateDialog_ArtifactId=Id TrustCertificateDialog_SelectAll=&Select All TrustCertificateDialog_DeselectAll=&Deselect All TrustCertificateDialog_ObjectType=Type -TrustCertificateDialog_Id=Id +TrustCertificateDialog_Id=Id/Fingerprint TrustCertificateDialog_Name=Name +TrustCertificateDialog_Classifier=Classifier +TrustCertificateDialog_CopyFingerprint=Copy Fingerprint +TrustCertificateDialog_dates=Validity Dates +TrustCertificateDialog_NotApplicable=n/a +TrustCertificateDialog_NotYetValidStartDate=\u274C Not yet valid, starts {0} +TrustCertificateDialog_expiredSince=\u274C Expired since {0} +TrustCertificateDialog_validExpires=\u2714\uFE0F Valid, expires {0} +TrustCertificateDialog_valid=\u2714\uFE0F Valid +TrustCertificateDialog_expired=\u274C Expired +TrustCertificateDialog_notYetValid=\u274C Not yet valid +TrustCertificateDialog_RememberSigners=Remember selected signers +TrustCertificateDialog_Unknown=Unknown +TrustCertificateDialog_Unsigned=Unsigned +TrustCertificateDialog_Version=Version diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/viewers/CertificateLabelProvider.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/viewers/CertificateLabelProvider.java index 9eb67d01e..39cc991d8 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/viewers/CertificateLabelProvider.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/viewers/CertificateLabelProvider.java @@ -14,10 +14,11 @@ package org.eclipse.equinox.internal.p2.ui.viewers; import java.security.cert.X509Certificate; -import org.bouncycastle.openpgp.PGPPublicKey; import org.eclipse.equinox.internal.provisional.security.ui.X500PrincipalHelper; +import org.eclipse.equinox.internal.provisional.security.ui.X509CertificateViewDialog; import org.eclipse.jface.viewers.*; import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Shell; /** * A label provider that displays X509 certificates. @@ -35,33 +36,12 @@ public class CertificateLabelProvider implements ILabelProvider { Object o = ((TreeNode) element).getValue(); if (o instanceof X509Certificate) { X509Certificate cert = (X509Certificate) o; - X500PrincipalHelper principalHelper = new X500PrincipalHelper(cert.getSubjectX500Principal()); - return principalHelper.getCN() + "; " + principalHelper.getOU() + "; " //$NON-NLS-1$ //$NON-NLS-2$ - + principalHelper.getO(); - } else if (o instanceof PGPPublicKey) { - return userFriendlyFingerPrint((PGPPublicKey) o); + return getText(cert); } } return ""; //$NON-NLS-1$ } - private String userFriendlyFingerPrint(PGPPublicKey key) { - if (key == null) { - return null; - } - StringBuilder builder = new StringBuilder(); - boolean spaceSuffix = false; - for (byte b : key.getFingerprint()) { - builder.append(String.format("%02X", Byte.toUnsignedInt(b))); //$NON-NLS-1$ - if (spaceSuffix) { - builder.append(' '); - } - spaceSuffix = !spaceSuffix; - } - builder.deleteCharAt(builder.length() - 1); - return builder.toString(); - } - @Override public void addListener(ILabelProviderListener listener) { // do nothing @@ -82,4 +62,23 @@ public class CertificateLabelProvider implements ILabelProvider { // do nothing } + /** + * Returns a string that can be used as readable label for a certificate. This + * hides the internal implementation classes needed to produce this label. + */ + public static String getText(X509Certificate cert) { + X500PrincipalHelper principalHelper = new X500PrincipalHelper(cert.getSubjectX500Principal()); + return principalHelper.getCN() + "; " + principalHelper.getOU() + "; " //$NON-NLS-1$ //$NON-NLS-2$ + + principalHelper.getO(); + } + + /** + * Opens a dialog to present detailed information about a certificate. This + * hides the internal implementation classes needed open this dialog. + */ + public static void openDialog(Shell shell, X509Certificate cert) { + // create and open dialog for certificate chain + X509CertificateViewDialog certificateViewDialog = new X509CertificateViewDialog(shell, cert); + certificateViewDialog.open(); + } } diff --git a/bundles/org.eclipse.equinox.p2.updatechecker/pom.xml b/bundles/org.eclipse.equinox.p2.updatechecker/pom.xml index da2f37456..49d08db7f 100644 --- a/bundles/org.eclipse.equinox.p2.updatechecker/pom.xml +++ b/bundles/org.eclipse.equinox.p2.updatechecker/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.p2.updatesite/pom.xml b/bundles/org.eclipse.equinox.p2.updatesite/pom.xml index 6f21a2913..1f7c8c962 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/pom.xml +++ b/bundles/org.eclipse.equinox.p2.updatesite/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/pom.xml b/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/pom.xml index ee6dc888a..43581bbeb 100644 --- a/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/pom.xml +++ b/bundles/org.eclipse.equinox.simpleconfigurator.manipulator/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml b/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml index 4f5b876cf..5a9a88217 100644 --- a/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml +++ b/bundles/org.eclipse.equinox.simpleconfigurator/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.discovery/META-INF/MANIFEST.MF b/examples/org.eclipse.equinox.p2.examples.rcp.discovery/META-INF/MANIFEST.MF index 2e13bd54c..9dffe88be 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.discovery/META-INF/MANIFEST.MF +++ b/examples/org.eclipse.equinox.p2.examples.rcp.discovery/META-INF/MANIFEST.MF @@ -26,8 +26,8 @@ Require-Bundle: org.eclipse.ui;bundle-version="3.6.0", org.eclipse.equinox.p2.discovery;bundle-version="1.0.0", org.eclipse.equinox.p2.discovery.compatibility;bundle-version="1.0.0", org.eclipse.equinox.p2.ui.discovery;bundle-version="1.0.0", - org.apache.httpcomponents.httpclient;bundle-version="4.2.0", - org.eclipse.ecf.provider.filetransfer.httpclient45;bundle-version="1.0.0" + org.apache.httpcomponents.client5.httpclient5;bundle-version="5.0.2", + org.eclipse.ecf.provider.filetransfer.httpclient5;bundle-version="1.0.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 Automatic-Module-Name: org.eclipse.equinox.p2.examples.rcp.cloud diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.discovery/cloud.product b/examples/org.eclipse.equinox.p2.examples.rcp.discovery/cloud.product index 0f39834a8..b7668e6ae 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.discovery/cloud.product +++ b/examples/org.eclipse.equinox.p2.examples.rcp.discovery/cloud.product @@ -46,8 +46,8 @@ <plugin id="org.apache.commons.jxpath"/> <plugin id="org.apache.commons.logging"/> <plugin id="org.apache.felix.scr"/> - <plugin id="org.apache.httpcomponents.httpclient"/> - <plugin id="org.apache.httpcomponents.httpcore"/> + <plugin id="org.apache.httpcomponents.client5.httpclient5"/> + <plugin id="org.apache.httpcomponents.core5.httpcore5"/> <plugin id="org.apache.xmlgraphics"/> <plugin id="org.eclipse.core.commands"/> <plugin id="org.eclipse.core.contenttype"/> @@ -85,7 +85,7 @@ <plugin id="org.eclipse.ecf.provider.filetransfer"/> <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient"/> <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient.ssl" fragment="true"/> - <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient45"/> + <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient5"/> <plugin id="org.eclipse.ecf.provider.filetransfer.ssl" fragment="true"/> <plugin id="org.eclipse.ecf.ssl" fragment="true"/> <plugin id="org.eclipse.emf.common"/> @@ -126,7 +126,7 @@ <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.security.ui"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> <plugin id="org.eclipse.equinox.simpleconfigurator.manipulator"/> diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.discovery/plugin.xml b/examples/org.eclipse.equinox.p2.examples.rcp.discovery/plugin.xml index bff2bb0e7..ee5676d55 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.discovery/plugin.xml +++ b/examples/org.eclipse.equinox.p2.examples.rcp.discovery/plugin.xml @@ -178,7 +178,7 @@ <overview screenshot="icons/mail-screenshot.png" summary="In computing, the Post Office Protocol (POP) is an application-layer Internet standard protocol used by local e-mail clients to retrieve e-mail from a remote server over a TCP/IP connection. POP and IMAP (Internet Message Access Protocol) are the two most prevalent Internet standard protocols for e-mail retrieval. Virtually all modern e-mail clients and servers support both. The POP protocol has been developed through several versions, with version 3 (POP3) being the current standard." - url="http://en.wikipedia.org/wiki/Post_Office_Protocol"> + url="https://en.wikipedia.org/wiki/Post_Office_Protocol"> </overview> <icon image32="icons/mail.png"> @@ -197,11 +197,11 @@ license="EPL" name="Eclipse Marketplace Client" provider="Eclipse Packaging Project" - siteUrl="http://download.eclipse.org/releases/staging"> + siteUrl="https://download.eclipse.org/releases/staging"> <overview screenshot="icons/mpc-screenshot.png" summary="Marketplace Client is a rich client solution for installing solutions listed on Eclipse Marketplace directly from an Eclipse Installation.

The Eclipse community has hundreds, if not thousands, of third-party plugins that users can add to their Eclipse installation. Unfortunately, there is not easy way to discover and install these solutions from within Eclipse.

The Eclipse Foundation has recently launched a new website, called Eclipse Marketplace, that provides a listing of Eclipse-based solutions. The listings allow each solution provider to specify a P2 repository for their solution. Eclipse users now have a central catalog to find Eclipse solutions but the install process is still not tightly integrated with the Eclipse workspace.

MPC provides the tight install integration between the Eclipse workspace and Eclipse Marketplace, plus other third party solution listings. MPC is included in many of the EPP packages." - url="http://eclipse.org/mpc/"></overview> + url="https://eclipse.org/mpc/"></overview> <icon image32="icons/mpc.png"> </icon> diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.discovery/pom.xml b/examples/org.eclipse.equinox.p2.examples.rcp.discovery/pom.xml index 563b6bbf0..a833132fc 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.discovery/pom.xml +++ b/examples/org.eclipse.equinox.p2.examples.rcp.discovery/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>org.eclipse.equinox.p2.examples</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.prestartupdate/META-INF/MANIFEST.MF b/examples/org.eclipse.equinox.p2.examples.rcp.prestartupdate/META-INF/MANIFEST.MF index 5207540c5..724d0234c 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.prestartupdate/META-INF/MANIFEST.MF +++ b/examples/org.eclipse.equinox.p2.examples.rcp.prestartupdate/META-INF/MANIFEST.MF @@ -13,7 +13,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ecf.filetransfer;bundle-version="3.0.0", org.eclipse.ecf.identity;bundle-version="3.0.0", org.eclipse.ecf.provider.filetransfer;bundle-version="3.0.0", - org.apache.httpcomponents.httpclient;bundle-version="4.2.0", - org.eclipse.ecf.provider.filetransfer.httpclient45;bundle-version="1.0.0" + org.apache.httpcomponents.client5.httpclient5;bundle-version="5.0.2", + org.eclipse.ecf.provider.filetransfer.httpclient5;bundle-version="1.0.0" Bundle-RequiredExecutionEnvironment: JavaSE-11 Automatic-Module-Name: org.eclipse.equinox.p2.examples.rcp.prestartupdate diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.prestartupdate/pom.xml b/examples/org.eclipse.equinox.p2.examples.rcp.prestartupdate/pom.xml index d686e45f7..9f13161d5 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.prestartupdate/pom.xml +++ b/examples/org.eclipse.equinox.p2.examples.rcp.prestartupdate/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>org.eclipse.equinox.p2.examples</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <artifactId>org.eclipse.equinox.p2.examples.rcp.prestartupdate</artifactId> diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/META-INF/MANIFEST.MF b/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/META-INF/MANIFEST.MF index bb82130a5..e2b034a24 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/META-INF/MANIFEST.MF +++ b/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/META-INF/MANIFEST.MF @@ -22,8 +22,8 @@ Require-Bundle: org.eclipse.ui;bundle-version="3.6.0", org.eclipse.ecf.filetransfer;bundle-version="3.0.0", org.eclipse.ecf.identity;bundle-version="3.0.0", org.eclipse.ecf.provider.filetransfer;bundle-version="3.0.0", - org.apache.httpcomponents.httpclient;bundle-version="4.2.0", - org.eclipse.ecf.provider.filetransfer.httpclient45;bundle-version="1.0.0" + org.apache.httpcomponents.client5.httpclient5;bundle-version="5.0.2", + org.eclipse.ecf.provider.filetransfer.httpclient5;bundle-version="1.0.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 Service-Component: OSGI-INF/policy_component.xml diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/pom.xml b/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/pom.xml index 1a3bc6f1e..7bd7d3088 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/pom.xml +++ b/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>org.eclipse.equinox.p2.examples</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <artifactId>org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility</artifactId> diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/sdkbundlevisibility.product b/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/sdkbundlevisibility.product index 730d77684..a1e4fc5dc 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/sdkbundlevisibility.product +++ b/examples/org.eclipse.equinox.p2.examples.rcp.sdkbundlevisibility/sdkbundlevisibility.product @@ -46,8 +46,8 @@ <plugin id="org.apache.commons.jxpath"/> <plugin id="org.apache.commons.logging"/> <plugin id="org.apache.felix.scr"/> - <plugin id="org.apache.httpcomponents.httpclient"/> - <plugin id="org.apache.httpcomponents.httpcore"/> + <plugin id="org.apache.httpcomponents.client5.httpclient5"/> + <plugin id="org.apache.httpcomponents.core5.httpcore5"/> <plugin id="org.apache.xmlgraphics"/> <plugin id="org.eclipse.core.commands"/> <plugin id="org.eclipse.core.contenttype"/> @@ -83,7 +83,7 @@ <plugin id="org.eclipse.ecf.filetransfer"/> <plugin id="org.eclipse.ecf.identity"/> <plugin id="org.eclipse.ecf.provider.filetransfer"/> - <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient45"/> + <plugin id="org.eclipse.ecf.provider.filetransfer.httpclient5"/> <plugin id="org.eclipse.ecf.provider.filetransfer.ssl" fragment="true"/> <plugin id="org.eclipse.ecf.ssl" fragment="true"/> <plugin id="org.eclipse.emf.common"/> @@ -119,7 +119,7 @@ <plugin id="org.eclipse.equinox.preferences"/> <plugin id="org.eclipse.equinox.registry"/> <plugin id="org.eclipse.equinox.security"/> - <plugin id="org.eclipse.equinox.security.linux.x86_64" fragment="true"/> + <plugin id="org.eclipse.equinox.security.linux" fragment="true"/> <plugin id="org.eclipse.equinox.security.macosx" fragment="true"/> <plugin id="org.eclipse.equinox.security.ui"/> <plugin id="org.eclipse.equinox.simpleconfigurator"/> diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.sdknoautoupdates/META-INF/MANIFEST.MF b/examples/org.eclipse.equinox.p2.examples.rcp.sdknoautoupdates/META-INF/MANIFEST.MF index 0ef9f7e8b..297dee540 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.sdknoautoupdates/META-INF/MANIFEST.MF +++ b/examples/org.eclipse.equinox.p2.examples.rcp.sdknoautoupdates/META-INF/MANIFEST.MF @@ -19,8 +19,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ecf.filetransfer;bundle-version="3.0.0", org.eclipse.ecf.identity;bundle-version="3.0.0", org.eclipse.ecf.provider.filetransfer;bundle-version="3.0.0", - org.apache.httpcomponents.httpclient;bundle-version="4.2.0", - org.eclipse.ecf.provider.filetransfer.httpclient45;bundle-version="1.0.0", + org.apache.httpcomponents.client5.httpclient5;bundle-version="5.0.2", + org.eclipse.ecf.provider.filetransfer.httpclient5;bundle-version="1.0.0", org.eclipse.equinox.p2.ui.sdk;bundle-version="1.0.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.sdknoautoupdates/pom.xml b/examples/org.eclipse.equinox.p2.examples.rcp.sdknoautoupdates/pom.xml index 588aa759e..024bb1e41 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.sdknoautoupdates/pom.xml +++ b/examples/org.eclipse.equinox.p2.examples.rcp.sdknoautoupdates/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>org.eclipse.equinox.p2.examples</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.sdkui/META-INF/MANIFEST.MF b/examples/org.eclipse.equinox.p2.examples.rcp.sdkui/META-INF/MANIFEST.MF index 8c0d5cd5b..417b58720 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.sdkui/META-INF/MANIFEST.MF +++ b/examples/org.eclipse.equinox.p2.examples.rcp.sdkui/META-INF/MANIFEST.MF @@ -21,8 +21,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.ecf.provider.filetransfer;bundle-version="3.0.0", org.eclipse.equinox.p2.ui.sdk;bundle-version="1.0.0", org.eclipse.equinox.p2.ui.sdk.scheduler;bundle-version="1.0.0", - org.apache.httpcomponents.httpclient;bundle-version="4.2.0", - org.eclipse.ecf.provider.filetransfer.httpclient45;bundle-version="1.0.0" + org.apache.httpcomponents.client5.httpclient5;bundle-version="5.0.2", + org.eclipse.ecf.provider.filetransfer.httpclient5;bundle-version="1.0.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 Automatic-Module-Name: org.eclipse.equinox.p2.examples.rcp.sdkui diff --git a/examples/org.eclipse.equinox.p2.examples.rcp.sdkui/pom.xml b/examples/org.eclipse.equinox.p2.examples.rcp.sdkui/pom.xml index e6352ad05..409e36705 100644 --- a/examples/org.eclipse.equinox.p2.examples.rcp.sdkui/pom.xml +++ b/examples/org.eclipse.equinox.p2.examples.rcp.sdkui/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>org.eclipse.equinox.p2.examples</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> diff --git a/examples/pom.xml b/examples/pom.xml index 4e10490f9..10e519663 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <artifactId>org.eclipse.equinox.p2.examples</artifactId> diff --git a/features/org.eclipse.equinox.p2.core.feature/feature.xml b/features/org.eclipse.equinox.p2.core.feature/feature.xml index f8cffb089..4b7c6ff2b 100644 --- a/features/org.eclipse.equinox.p2.core.feature/feature.xml +++ b/features/org.eclipse.equinox.p2.core.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.equinox.p2.core.feature" label="%featureName" - version="1.6.1300.qualifier" + version="1.6.1400.qualifier" provider-name="%providerName" license-feature="org.eclipse.license" license-feature-version="0.0.0"> @@ -23,7 +23,7 @@ <import feature="org.eclipse.ecf.core.feature" version="1.4.0" match="compatible"/> <import feature="org.eclipse.ecf.core.ssl.feature" version="1.1.0" match="compatible"/> <import feature="org.eclipse.ecf.filetransfer.feature" version="3.13.7" match="compatible"/> - <import feature="org.eclipse.ecf.filetransfer.httpclient45.feature" version="1.0.0" match="compatible"/> + <import feature="org.eclipse.ecf.filetransfer.httpclient5.feature" version="1.0.0" match="compatible"/> <import feature="org.eclipse.ecf.filetransfer.ssl.feature" version="1.1.0" match="compatible"/> </requires> @@ -184,7 +184,7 @@ unpack="false"/> <plugin - id="org.eclipse.equinox.security.linux.x86_64" + id="org.eclipse.equinox.security.linux" os="linux" arch="x86_64" download-size="0" diff --git a/features/org.eclipse.equinox.p2.core.feature/forceQualifierUpdate.txt b/features/org.eclipse.equinox.p2.core.feature/forceQualifierUpdate.txt index 69401c095..55820793c 100644 --- a/features/org.eclipse.equinox.p2.core.feature/forceQualifierUpdate.txt +++ b/features/org.eclipse.equinox.p2.core.feature/forceQualifierUpdate.txt @@ -22,4 +22,5 @@ Bug 553238 - Unanticipated comparator errors in I20191119-1800 Bug 477007 - Do not use whitespace in XML Bug 574602 - Eclipse 4.21 prerequisites: Orbit Bug 574602 - Eclipse 4.21 prerequisites: Orbit -Bug 576389 - Touch bundle for org.bouncycastle.bcprov version update
\ No newline at end of file +Bug 576389 - Touch bundle for org.bouncycastle.bcprov version update +Bug 578120 - Update to latest Orbit - bouncycastle and mina sshd updates
\ No newline at end of file diff --git a/features/org.eclipse.equinox.p2.core.feature/pom.xml b/features/org.eclipse.equinox.p2.core.feature/pom.xml index 1420cd178..ebb4950cb 100644 --- a/features/org.eclipse.equinox.p2.core.feature/pom.xml +++ b/features/org.eclipse.equinox.p2.core.feature/pom.xml @@ -15,12 +15,12 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.core.feature</artifactId> - <version>1.6.1300-SNAPSHOT</version> + <version>1.6.1400-SNAPSHOT</version> <packaging>eclipse-feature</packaging> diff --git a/features/org.eclipse.equinox.p2.discovery.feature/feature.xml b/features/org.eclipse.equinox.p2.discovery.feature/feature.xml index 5a831e43e..49c7f0d02 100644 --- a/features/org.eclipse.equinox.p2.discovery.feature/feature.xml +++ b/features/org.eclipse.equinox.p2.discovery.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.equinox.p2.discovery.feature" label="%featureName" - version="1.2.1000.qualifier" + version="1.2.1100.qualifier" provider-name="%providerName" license-feature="org.eclipse.license" license-feature-version="0.0.0"> diff --git a/features/org.eclipse.equinox.p2.discovery.feature/pom.xml b/features/org.eclipse.equinox.p2.discovery.feature/pom.xml index 0a7060712..f63571e19 100644 --- a/features/org.eclipse.equinox.p2.discovery.feature/pom.xml +++ b/features/org.eclipse.equinox.p2.discovery.feature/pom.xml @@ -14,12 +14,12 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.discovery.feature</artifactId> - <version>1.2.1000-SNAPSHOT</version> + <version>1.2.1100-SNAPSHOT</version> <packaging>eclipse-feature</packaging> <build> diff --git a/features/org.eclipse.equinox.p2.extras.feature/feature.xml b/features/org.eclipse.equinox.p2.extras.feature/feature.xml index ac67f7b30..8989b7e84 100644 --- a/features/org.eclipse.equinox.p2.extras.feature/feature.xml +++ b/features/org.eclipse.equinox.p2.extras.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.equinox.p2.extras.feature" label="%featureName" - version="1.4.1500.qualifier" + version="1.4.1600.qualifier" provider-name="%providerName" license-feature="org.eclipse.license" license-feature-version="0.0.0"> diff --git a/features/org.eclipse.equinox.p2.extras.feature/pom.xml b/features/org.eclipse.equinox.p2.extras.feature/pom.xml index 200e682ad..97e83185a 100644 --- a/features/org.eclipse.equinox.p2.extras.feature/pom.xml +++ b/features/org.eclipse.equinox.p2.extras.feature/pom.xml @@ -14,12 +14,12 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.extras.feature</artifactId> - <version>1.4.1500-SNAPSHOT</version> + <version>1.4.1600-SNAPSHOT</version> <packaging>eclipse-feature</packaging> <build> diff --git a/features/org.eclipse.equinox.p2.rcp.feature/feature.xml b/features/org.eclipse.equinox.p2.rcp.feature/feature.xml index f07c42fa7..1fc40357a 100644 --- a/features/org.eclipse.equinox.p2.rcp.feature/feature.xml +++ b/features/org.eclipse.equinox.p2.rcp.feature/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.equinox.p2.rcp.feature" label="%featureName" - version="1.4.1500.qualifier" + version="1.4.1600.qualifier" provider-name="%providerName" license-feature="org.eclipse.license" license-feature-version="0.0.0"> diff --git a/features/org.eclipse.equinox.p2.rcp.feature/pom.xml b/features/org.eclipse.equinox.p2.rcp.feature/pom.xml index 7abade620..c25b7dafe 100644 --- a/features/org.eclipse.equinox.p2.rcp.feature/pom.xml +++ b/features/org.eclipse.equinox.p2.rcp.feature/pom.xml @@ -14,13 +14,13 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.rcp.feature</artifactId> - <version>1.4.1500-SNAPSHOT</version> + <version>1.4.1600-SNAPSHOT</version> <packaging>eclipse-feature</packaging> <build> diff --git a/features/org.eclipse.equinox.p2.sdk/feature.xml b/features/org.eclipse.equinox.p2.sdk/feature.xml index 3f87a10f4..837fb5ea1 100644 --- a/features/org.eclipse.equinox.p2.sdk/feature.xml +++ b/features/org.eclipse.equinox.p2.sdk/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.equinox.p2.sdk" label="%featureName" - version="3.11.1500.qualifier" + version="3.11.1600.qualifier" provider-name="%providerName" license-feature="org.eclipse.license" license-feature-version="0.0.0"> @@ -31,7 +31,7 @@ <import feature="org.eclipse.ecf.core.feature.source" version="1.4.0" match="compatible"/> <import feature="org.eclipse.ecf.core.ssl.feature.source" version="1.1.0" match="compatible"/> <import feature="org.eclipse.ecf.filetransfer.feature.source" version="3.13.7" match="compatible"/> - <import feature="org.eclipse.ecf.filetransfer.httpclient45.feature.source" version="1.0.0" match="compatible"/> + <import feature="org.eclipse.ecf.filetransfer.httpclient5.feature.source" version="1.0.0" match="compatible"/> <import feature="org.eclipse.ecf.filetransfer.ssl.feature.source" version="1.1.0" match="compatible"/> </requires> diff --git a/features/org.eclipse.equinox.p2.sdk/pom.xml b/features/org.eclipse.equinox.p2.sdk/pom.xml index ce34c2cd4..6e36baf37 100644 --- a/features/org.eclipse.equinox.p2.sdk/pom.xml +++ b/features/org.eclipse.equinox.p2.sdk/pom.xml @@ -15,11 +15,11 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.sdk</artifactId> - <version>3.11.1500-SNAPSHOT</version> + <version>3.11.1600-SNAPSHOT</version> <packaging>eclipse-feature</packaging> </project> diff --git a/features/org.eclipse.equinox.p2.user.ui/feature.xml b/features/org.eclipse.equinox.p2.user.ui/feature.xml index 415f30d94..8e7849ace 100644 --- a/features/org.eclipse.equinox.p2.user.ui/feature.xml +++ b/features/org.eclipse.equinox.p2.user.ui/feature.xml @@ -2,7 +2,7 @@ <feature id="org.eclipse.equinox.p2.user.ui" label="%featureName" - version="2.4.1500.qualifier" + version="2.4.1600.qualifier" provider-name="%providerName" license-feature="org.eclipse.license" license-feature-version="0.0.0"> diff --git a/features/org.eclipse.equinox.p2.user.ui/pom.xml b/features/org.eclipse.equinox.p2.user.ui/pom.xml index 7840daa87..e05ba4722 100644 --- a/features/org.eclipse.equinox.p2.user.ui/pom.xml +++ b/features/org.eclipse.equinox.p2.user.ui/pom.xml @@ -14,13 +14,13 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.user.ui</artifactId> - <version>2.4.1500-SNAPSHOT</version> + <version>2.4.1600-SNAPSHOT</version> <packaging>eclipse-feature</packaging> <build> diff --git a/org.eclipse.equinox.p2.releng/default.target b/org.eclipse.equinox.p2.releng/default.target index d3e8fe8bc..5139a8a19 100644 --- a/org.eclipse.equinox.p2.releng/default.target +++ b/org.eclipse.equinox.p2.releng/default.target @@ -6,7 +6,7 @@ <unit id="org.eclipse.platform.sdk" version="0.0.0"/> <unit id="org.eclipse.core.tests.harness" version="0.0.0"/> <unit id="org.eclipse.test.feature.group" version="0.0.0"/> -<repository location="https://download.eclipse.org/eclipse/updates/4.23-I-builds"/> +<repository location="https://download.eclipse.org/eclipse/updates/4.24-I-builds"/> </location> <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit"> <unit id="org.bouncycastle.bcprov" version="0.0.0"/> diff --git a/org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent/pom.xml b/org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent/pom.xml index f0b5aaf3c..d2d0472a3 100644 --- a/org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent/pom.xml +++ b/org.eclipse.equinox.p2.releng/org.eclipse.equinox.p2.tests-parent/pom.xml @@ -14,7 +14,7 @@ <parent> <groupId>org.eclipse.equinox.p2</groupId> <artifactId>rt.equinox.p2</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../..</relativePath> </parent> <groupId>org.eclipse</groupId> @@ -15,7 +15,7 @@ <parent> <groupId>org.eclipse</groupId> <artifactId>eclipse-platform-parent</artifactId> - <version>4.23.0-SNAPSHOT</version> + <version>4.24.0-SNAPSHOT</version> <relativePath>../eclipse-platform-parent</relativePath> </parent> |