1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.connector.basic;
20
21 import java.io.IOException;
22 import java.io.UncheckedIOException;
23 import java.net.URI;
24 import java.nio.file.Path;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.Map;
28
29 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
30 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
31 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind;
32 import org.eclipse.aether.spi.connector.layout.RepositoryLayout.ChecksumLocation;
33 import org.eclipse.aether.spi.io.ChecksumProcessor;
34 import org.eclipse.aether.transfer.ChecksumFailureException;
35 import org.eclipse.aether.util.FileUtils;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39
40
41
42 final class ChecksumValidator {
43
44 interface ChecksumFetcher {
45
46
47
48
49
50 boolean fetchChecksum(URI remote, Path local) throws Exception;
51 }
52
53 private static final Logger LOGGER = LoggerFactory.getLogger(ChecksumValidator.class);
54
55 private final Path dataPath;
56
57 private final Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories;
58
59 private final ChecksumProcessor checksumProcessor;
60
61 private final ChecksumFetcher checksumFetcher;
62
63 private final ChecksumPolicy checksumPolicy;
64
65 private final Map<String, String> providedChecksums;
66
67 private final Collection<ChecksumLocation> checksumLocations;
68
69 private final Map<Path, String> checksumExpectedValues;
70
71 ChecksumValidator(
72 Path dataPath,
73 Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
74 ChecksumProcessor checksumProcessor,
75 ChecksumFetcher checksumFetcher,
76 ChecksumPolicy checksumPolicy,
77 Map<String, String> providedChecksums,
78 Collection<ChecksumLocation> checksumLocations) {
79 this.dataPath = dataPath;
80 this.checksumAlgorithmFactories = checksumAlgorithmFactories;
81 this.checksumProcessor = checksumProcessor;
82 this.checksumFetcher = checksumFetcher;
83 this.checksumPolicy = checksumPolicy;
84 this.providedChecksums = providedChecksums;
85 this.checksumLocations = checksumLocations;
86 this.checksumExpectedValues = new HashMap<>();
87 }
88
89 public ChecksumCalculator newChecksumCalculator(Path targetFile) {
90 if (checksumPolicy != null) {
91 return ChecksumCalculator.newInstance(targetFile, checksumAlgorithmFactories);
92 }
93 return null;
94 }
95
96 public void validate(Map<String, ?> actualChecksums, Map<String, ?> includedChecksums)
97 throws ChecksumFailureException {
98 if (checksumPolicy == null) {
99 return;
100 }
101 if (providedChecksums != null && validateChecksums(actualChecksums, ChecksumKind.PROVIDED, providedChecksums)) {
102 return;
103 }
104 if (includedChecksums != null
105 && validateChecksums(actualChecksums, ChecksumKind.REMOTE_INCLUDED, includedChecksums)) {
106 return;
107 }
108 if (!checksumLocations.isEmpty()) {
109 if (validateExternalChecksums(actualChecksums)) {
110 return;
111 }
112 checksumPolicy.onNoMoreChecksums();
113 }
114 }
115
116 private boolean validateChecksums(Map<String, ?> actualChecksums, ChecksumKind kind, Map<String, ?> checksums)
117 throws ChecksumFailureException {
118 for (Map.Entry<String, ?> entry : checksums.entrySet()) {
119 String algo = entry.getKey();
120 Object calculated = actualChecksums.get(algo);
121 if (!(calculated instanceof String)) {
122 continue;
123 }
124 ChecksumAlgorithmFactory checksumAlgorithmFactory = checksumAlgorithmFactories.stream()
125 .filter(a -> a.getName().equals(algo))
126 .findFirst()
127 .orElse(null);
128 if (checksumAlgorithmFactory == null) {
129 continue;
130 }
131
132 String actual = String.valueOf(calculated);
133 String expected = entry.getValue().toString();
134 checksumExpectedValues.put(getChecksumPath(checksumAlgorithmFactory), expected);
135
136 if (!isEqualChecksum(expected, actual)) {
137 checksumPolicy.onChecksumMismatch(
138 checksumAlgorithmFactory.getName(),
139 kind,
140 new ChecksumFailureException(expected, kind.name(), actual));
141 } else if (checksumPolicy.onChecksumMatch(checksumAlgorithmFactory.getName(), kind)) {
142 return true;
143 }
144 }
145 return false;
146 }
147
148 private boolean validateExternalChecksums(Map<String, ?> actualChecksums) throws ChecksumFailureException {
149 for (ChecksumLocation checksumLocation : checksumLocations) {
150 ChecksumAlgorithmFactory factory = checksumLocation.getChecksumAlgorithmFactory();
151 Object calculated = actualChecksums.get(factory.getName());
152 if (calculated instanceof Exception) {
153 checksumPolicy.onChecksumError(
154 factory.getName(), ChecksumKind.REMOTE_EXTERNAL, new ChecksumFailureException((Exception)
155 calculated));
156 continue;
157 }
158 Path checksumFile = getChecksumPath(checksumLocation.getChecksumAlgorithmFactory());
159 try (FileUtils.TempFile tempFile = FileUtils.newTempFile()) {
160 Path tmp = tempFile.getPath();
161 try {
162 if (!checksumFetcher.fetchChecksum(checksumLocation.getLocation(), tmp)) {
163 continue;
164 }
165 } catch (Exception e) {
166 checksumPolicy.onChecksumError(
167 factory.getName(), ChecksumKind.REMOTE_EXTERNAL, new ChecksumFailureException(e));
168 continue;
169 }
170
171 String actual = String.valueOf(calculated);
172 String expected = checksumProcessor.readChecksum(tmp);
173 checksumExpectedValues.put(checksumFile, expected);
174
175 if (!isEqualChecksum(expected, actual)) {
176 checksumPolicy.onChecksumMismatch(
177 factory.getName(),
178 ChecksumKind.REMOTE_EXTERNAL,
179 new ChecksumFailureException(expected, ChecksumKind.REMOTE_EXTERNAL.name(), actual));
180 } else if (checksumPolicy.onChecksumMatch(factory.getName(), ChecksumKind.REMOTE_EXTERNAL)) {
181 return true;
182 }
183 } catch (IOException e) {
184 checksumPolicy.onChecksumError(
185 factory.getName(), ChecksumKind.REMOTE_EXTERNAL, new ChecksumFailureException(e));
186 }
187 }
188 return false;
189 }
190
191 private static boolean isEqualChecksum(String expected, String actual) {
192 return expected.equalsIgnoreCase(actual);
193 }
194
195 private Path getChecksumPath(ChecksumAlgorithmFactory factory) {
196 return dataPath.getParent().resolve(dataPath.getFileName() + "." + factory.getFileExtension());
197 }
198
199 public void retry() {
200 checksumPolicy.onTransferRetry();
201 checksumExpectedValues.clear();
202 }
203
204 public boolean handle(ChecksumFailureException exception) {
205 return checksumPolicy.onTransferChecksumFailure(exception);
206 }
207
208 public void commit() {
209 for (Map.Entry<Path, String> entry : checksumExpectedValues.entrySet()) {
210 Path checksumFile = entry.getKey();
211 try {
212 checksumProcessor.writeChecksum(checksumFile, entry.getValue());
213 } catch (IOException e) {
214 LOGGER.debug("Failed to write checksum file {}", checksumFile, e);
215 throw new UncheckedIOException(e);
216 }
217 }
218 checksumExpectedValues.clear();
219 }
220 }