| /******************************************************************************* |
| * Copyright (c) 2015, 2018 Mykola Nikishov. |
| * |
| * 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: |
| * Mykola Nikishov - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.artifact.processors.checksum; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.*; |
| import java.util.Map.Entry; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.equinox.internal.p2.artifact.processors.md5.MD5Verifier; |
| import org.eclipse.equinox.internal.p2.artifact.repository.Messages; |
| import org.eclipse.equinox.internal.p2.repository.Activator; |
| import org.eclipse.equinox.internal.p2.repository.helpers.ChecksumHelper; |
| import org.eclipse.equinox.internal.p2.repository.helpers.ChecksumProducer; |
| import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStep; |
| import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; |
| import org.eclipse.osgi.util.NLS; |
| |
| public class ChecksumUtilities { |
| |
| private static final String ARTIFACT_CHECKSUMS_POINT = "org.eclipse.equinox.p2.artifact.repository.artifactChecksums"; //$NON-NLS-1$ |
| private static final String MD5_MESSAGE_DIGEST = "MD5"; //$NON-NLS-1$ |
| |
| /** |
| * Instances of checksum verifiers applicable for the artifact descriptor |
| * |
| * @param descriptor |
| * @param property either {@link IArtifactDescriptor#ARTIFACT_CHECKSUM} or {@link IArtifactDescriptor#DOWNLOAD_CHECKSUM} |
| * @param checksumsToSkip |
| * @return list of checksum verifiers |
| * @throws IllegalArgumentException if property neither {@link IArtifactDescriptor#ARTIFACT_CHECKSUM} nor {@link IArtifactDescriptor#DOWNLOAD_CHECKSUM} |
| * @see ChecksumHelper#getChecksums(IArtifactDescriptor, String) |
| */ |
| public static Collection<ProcessingStep> getChecksumVerifiers(IArtifactDescriptor descriptor, String property, Set<String> checksumsToSkip) throws IllegalArgumentException { |
| Collection<ProcessingStep> steps = new ArrayList<>(); |
| Map<String, String> checksums = ChecksumHelper.getChecksums(descriptor, property); |
| |
| IConfigurationElement[] checksumVerifierConfigurations = getChecksumComparatorConfigurations(); |
| |
| for (Entry<String, String> checksumEntry : checksums.entrySet()) { |
| if (checksumsToSkip.contains(checksumEntry.getKey())) |
| continue; |
| |
| for (IConfigurationElement checksumVerifierConfiguration : checksumVerifierConfigurations) { |
| String checksumId = checksumVerifierConfiguration.getAttribute("id"); //$NON-NLS-1$ |
| if (checksumEntry.getKey().equals(checksumId)) { |
| String checksumAlgorithm = checksumVerifierConfiguration.getAttribute("algorithm"); //$NON-NLS-1$ |
| ChecksumVerifier checksumVerifier = new ChecksumVerifier(checksumAlgorithm, checksumId, checksumEntry.getValue()); |
| if (checksumVerifier.getStatus().isOK()) |
| steps.add(checksumVerifier); |
| else |
| // TODO log something? |
| continue; |
| } |
| } |
| } |
| |
| Optional<MD5Verifier> legacyMd5Verifier = getLegacyMd5Verifier(descriptor, property); |
| legacyMd5Verifier.ifPresent(verifier -> steps.add(verifier)); |
| |
| return steps; |
| } |
| |
| public static IConfigurationElement[] getChecksumComparatorConfigurations() { |
| return RegistryFactory.getRegistry().getConfigurationElementsFor(ARTIFACT_CHECKSUMS_POINT); |
| } |
| |
| /** |
| * Caller is responsible for checking the returned status and decide if problems are fatal or not. |
| * |
| * @param pathOnDisk file to calculate checksums for |
| * @param checksums calculated checksums |
| * @param checksumsToSkip |
| * @return status |
| */ |
| public static IStatus calculateChecksums(File pathOnDisk, Map<String, String> checksums, Collection<String> checksumsToSkip) { |
| // TODO pathOnDisk.getAbsolutePath() || pathOnDisk.getCanonicalPath() |
| MultiStatus status = new MultiStatus(Activator.ID, IStatus.OK, NLS.bind(Messages.calculateChecksum_file, pathOnDisk.getAbsolutePath()), null); |
| for (IConfigurationElement checksumVerifierConfiguration : ChecksumUtilities.getChecksumComparatorConfigurations()) { |
| String id = checksumVerifierConfiguration.getAttribute("id"); //$NON-NLS-1$ |
| if (checksumsToSkip.contains(id)) |
| // don't calculate checksum if algo is disabled |
| continue; |
| String algorithm = checksumVerifierConfiguration.getAttribute("algorithm"); //$NON-NLS-1$ |
| Optional<String> checksum = calculateChecksum(pathOnDisk, status, id, algorithm); |
| checksum.ifPresent(c -> checksums.put(id, c)); |
| } |
| |
| boolean doNotSkipMd5 = !checksumsToSkip.contains(ChecksumHelper.MD5); |
| if (doNotSkipMd5) { |
| Optional<String> md5 = calculateChecksum(pathOnDisk, status, ChecksumHelper.MD5, MD5_MESSAGE_DIGEST); |
| md5.ifPresent(c -> checksums.put(ChecksumHelper.MD5, c)); |
| } |
| |
| return status; |
| } |
| |
| private static Optional<String> calculateChecksum(File pathOnDisk, MultiStatus status, String id, String algorithm) { |
| try { |
| String checksum = ChecksumProducer.produce(pathOnDisk, algorithm); |
| String message = NLS.bind(Messages.calculateChecksum_ok, new Object[] {id, algorithm, checksum}); |
| status.add(new Status(IStatus.OK, Activator.ID, message)); |
| return Optional.of(checksum); |
| } catch (NoSuchAlgorithmException e) { |
| String message = NLS.bind(Messages.calculateChecksum_error, id, algorithm); |
| status.add(new Status(IStatus.ERROR, Activator.ID, message, e)); |
| } catch (IOException e) { |
| String message = NLS.bind(Messages.calculateChecksum_error, id, algorithm); |
| status.add(new Status(IStatus.ERROR, Activator.ID, message, e)); |
| } |
| return Optional.empty(); |
| } |
| |
| /** |
| * @param property either {@link IArtifactDescriptor#ARTIFACT_CHECKSUM} or {@link IArtifactDescriptor#DOWNLOAD_CHECKSUM} |
| * @param checksums |
| */ |
| public static Map<String, String> checksumsToProperties(String property, Map<String, String> checksums) { |
| HashMap<String, String> properties = new HashMap<>(); |
| for (Entry<String, String> checksum : checksums.entrySet()) { |
| properties.put(String.join(".", property, checksum.getKey()), checksum.getValue()); //$NON-NLS-1$ |
| } |
| |
| putLegacyMd5Property(property, checksums, properties); |
| |
| return properties; |
| } |
| |
| private static Optional<MD5Verifier> getLegacyMd5Verifier(IArtifactDescriptor descriptor, String propertyNamespace) { |
| String md5 = null; |
| switch (propertyNamespace) { |
| case IArtifactDescriptor.ARTIFACT_CHECKSUM : |
| md5 = descriptor.getProperty(IArtifactDescriptor.ARTIFACT_MD5); |
| break; |
| case IArtifactDescriptor.DOWNLOAD_CHECKSUM : |
| md5 = descriptor.getProperty(IArtifactDescriptor.DOWNLOAD_MD5); |
| break; |
| default : |
| throw new IllegalArgumentException(propertyNamespace); |
| } |
| |
| if (md5 != null) { |
| @SuppressWarnings("resource") //It's used later so shouldn't be closed |
| MD5Verifier checksumVerifier = new MD5Verifier(md5); |
| if (checksumVerifier.getStatus().isOK()) |
| return Optional.of(checksumVerifier); |
| } |
| |
| return Optional.empty(); |
| } |
| |
| private static void putLegacyMd5Property(String propertyNamespace, Map<String, String> checksums, HashMap<String, String> result) { |
| String md5 = checksums.get(ChecksumHelper.MD5); |
| if (md5 != null) { |
| if (IArtifactDescriptor.ARTIFACT_CHECKSUM.equals(propertyNamespace)) |
| result.put(IArtifactDescriptor.ARTIFACT_MD5, md5); |
| if (IArtifactDescriptor.DOWNLOAD_CHECKSUM.equals(propertyNamespace)) |
| result.put(IArtifactDescriptor.DOWNLOAD_MD5, md5); |
| } |
| } |
| } |