* under the License. | * under the License. | ||||
*/ | */ | ||||
import org.apache.commons.io.IOUtils; | |||||
import org.apache.commons.io.output.NullOutputStream; | |||||
import java.io.FileNotFoundException; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | |||||
import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||
import java.nio.MappedByteBuffer; | import java.nio.MappedByteBuffer; | ||||
import java.nio.channels.FileChannel; | import java.nio.channels.FileChannel; | ||||
import java.nio.file.Path; | import java.nio.file.Path; | ||||
import java.nio.file.StandardOpenOption; | import java.nio.file.StandardOpenOption; | ||||
import java.security.DigestInputStream; | |||||
import java.security.MessageDigest; | import java.security.MessageDigest; | ||||
import java.security.NoSuchAlgorithmException; | import java.security.NoSuchAlgorithmException; | ||||
import java.util.List; | |||||
/** | /** | ||||
* Checksum - simple checksum hashing routines. | * Checksum - simple checksum hashing routines. | ||||
*/ | */ | ||||
public class Checksum | public class Checksum | ||||
{ | { | ||||
private static final int BUFFER_SIZE = 32768; | |||||
private byte[] result = new byte[0]; | private byte[] result = new byte[0]; | ||||
public static void update( List<Checksum> checksums, Path file ) | |||||
throws ChecksumValidationException | |||||
{ | |||||
long fileSize; | |||||
try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ )) { | |||||
fileSize = channel.size(); | |||||
long pos = 0; | |||||
while (pos<fileSize) | |||||
{ | |||||
long bufferSize = Math.min(BUFFER_SIZE, fileSize-pos); | |||||
MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_ONLY, pos, bufferSize); | |||||
for ( Checksum checksum : checksums ) | |||||
{ | |||||
checksum.update( buffer ); | |||||
buffer.rewind(); | |||||
} | |||||
fileSize = channel.size(); | |||||
pos += BUFFER_SIZE; | |||||
} | |||||
for (Checksum checksum : checksums) { | |||||
checksum.finish(); | |||||
} | |||||
} catch(FileNotFoundException e) | |||||
{ | |||||
throw new ChecksumValidationException( ChecksumValidationException.ValidationError.FILE_NOT_FOUND, "File that should be parsed, not found: "+e.getMessage(), e ); | |||||
} catch(IOException e) { | |||||
throw new ChecksumValidationException( ChecksumValidationException.ValidationError.READ_ERROR, "Parsing of file failed: "+e.getMessage(), e ); | |||||
} | |||||
} | |||||
private final MessageDigest md; | private final MessageDigest md; | ||||
private ChecksumAlgorithm checksumAlgorithm; | private ChecksumAlgorithm checksumAlgorithm; | ||||
return this; | return this; | ||||
} | } | ||||
public void update( Path file ) | |||||
throws IOException | |||||
{ | |||||
long fileSize; | |||||
try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ )) { | |||||
fileSize = channel.size(); | |||||
long pos = 0; | |||||
while (pos<fileSize) | |||||
{ | |||||
long bufferSize = Math.min(BUFFER_SIZE, fileSize-pos); | |||||
MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_ONLY, pos, bufferSize); | |||||
update( buffer ); | |||||
buffer.rewind(); | |||||
fileSize = channel.size(); | |||||
pos += BUFFER_SIZE; | |||||
} | |||||
finish(); | |||||
} | |||||
} | |||||
public boolean compare(byte[] cmp) { | public boolean compare(byte[] cmp) { | ||||
if (this.result == null || this.result.length==0) { | if (this.result == null || this.result.length==0) { | ||||
finish(); | finish(); |
/** | /** | ||||
* Enumeration of available ChecksumAlgorithm techniques. | * Enumeration of available ChecksumAlgorithm techniques. | ||||
* | * | ||||
* Each algorithm represents a message digest algorithm and has a unique type. | |||||
* The type string may be used in the hash files (FreeBSD and OpenSSL add the type to the hash file) | |||||
* | |||||
* There are multiple file extensions. The first one is considered the default extension. | |||||
* | * | ||||
*/ | */ | ||||
public enum ChecksumAlgorithm { | public enum ChecksumAlgorithm { | ||||
MD5("MD5", "MD5", "md5"), | MD5("MD5", "MD5", "md5"), | ||||
SHA1("SHA-1", "SHA1", "sha1", "sha128", "sha-128"), | SHA1("SHA-1", "SHA1", "sha1", "sha128", "sha-128"), | ||||
SHA256("SHA-256", "SHA2", "sha2", "sha256", "sha-256"), | |||||
SHA384("SHA-384", "SHA3", "sha3", "sha384", "sha-384"), | |||||
SHA512("SHA-512", "SHA5", "sha5", "sha512", "sha-512"); | |||||
SHA256("SHA-256", "SHA256", "sha256", "sha2", "sha-256"), | |||||
SHA384("SHA-384", "SHA384", "sha384", "sha3", "sha-384"), | |||||
SHA512("SHA-512", "SHA512", "sha512", "sha5", "sha-512"); | |||||
public static ChecksumAlgorithm getByExtension( Path file ) | public static ChecksumAlgorithm getByExtension( Path file ) | ||||
{ | { | ||||
for (ChecksumAlgorithm alg : ChecksumAlgorithm.values()) { | for (ChecksumAlgorithm alg : ChecksumAlgorithm.values()) { | ||||
for (String extString : alg.getExt()) | for (String extString : alg.getExt()) | ||||
{ | { | ||||
extensionMap.put( extString, alg ); | |||||
extensionMap.put( extString.toLowerCase(), alg ); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
public static Set<String> getExtensions() { | |||||
public static Set<String> getAllExtensions() { | |||||
return extensionMap.keySet(); | return extensionMap.keySet(); | ||||
} | } | ||||
private final String algorithm; | private final String algorithm; | ||||
/** | /** | ||||
* The file extension for this ChecksumAlgorithm. | |||||
* The file extensions for this ChecksumAlgorithm. | |||||
*/ | */ | ||||
private final List<String> ext; | private final List<String> ext; | ||||
* Construct a ChecksumAlgorithm | * Construct a ChecksumAlgorithm | ||||
* | * | ||||
* @param algorithm the MessageDigest algorithm | * @param algorithm the MessageDigest algorithm | ||||
* @param ext the file extension. | |||||
* @param type the checksum type. | |||||
* @param type a unique identifier for the type | |||||
* @param ext the list of file extensions | |||||
*/ | */ | ||||
private ChecksumAlgorithm( String algorithm, String type, String... ext ) | private ChecksumAlgorithm( String algorithm, String type, String... ext ) | ||||
{ | { | ||||
} | } | ||||
/** | |||||
* Returns the message digest algorithm identifier | |||||
* @return | |||||
*/ | |||||
public String getAlgorithm() | public String getAlgorithm() | ||||
{ | { | ||||
return algorithm; | return algorithm; | ||||
} | } | ||||
/** | |||||
* Returns the list of extensions | |||||
* @return | |||||
*/ | |||||
public List<String> getExt() | public List<String> getExt() | ||||
{ | { | ||||
return ext; | return ext; | ||||
} | } | ||||
/** | |||||
* Returns the checksum identifier | |||||
* @return | |||||
*/ | |||||
public String getType() | public String getType() | ||||
{ | { | ||||
return type; | return type; | ||||
} | } | ||||
/** | |||||
* Returns the default extension of the current algorithm | |||||
* @return | |||||
*/ | |||||
public String getDefaultExtension() { | |||||
return ext.get(0); | |||||
} | |||||
} | } |
package org.apache.archiva.checksum; | |||||
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
import java.io.FileNotFoundException; | |||||
import java.io.IOException; | |||||
import java.nio.MappedByteBuffer; | |||||
import java.nio.channels.FileChannel; | |||||
import java.nio.file.Path; | |||||
import java.nio.file.StandardOpenOption; | |||||
import java.util.List; | |||||
import java.util.stream.Collectors; | |||||
/** | |||||
* Utility class that handles multiple checksums for a single file. | |||||
*/ | |||||
public class ChecksumUtil { | |||||
static final int BUFFER_SIZE = 32768; | |||||
public static void update(List<Checksum> checksumList, Path file ) throws IOException { | |||||
long fileSize; | |||||
try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ )) { | |||||
fileSize = channel.size(); | |||||
long pos = 0; | |||||
while (pos < fileSize) { | |||||
long bufferSize = Math.min(BUFFER_SIZE, fileSize - pos); | |||||
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, pos, bufferSize); | |||||
for (Checksum checksum : checksumList) { | |||||
checksum.update(buffer); | |||||
buffer.rewind(); | |||||
} | |||||
fileSize = channel.size(); | |||||
pos += BUFFER_SIZE; | |||||
} | |||||
for (Checksum checksum : checksumList) { | |||||
checksum.finish(); | |||||
} | |||||
} | |||||
} | |||||
public static void update(Checksum checksum, Path file) | |||||
throws IOException | |||||
{ | |||||
long fileSize; | |||||
try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ )) { | |||||
fileSize = channel.size(); | |||||
long pos = 0; | |||||
while (pos<fileSize) | |||||
{ | |||||
long bufferSize = Math.min(BUFFER_SIZE, fileSize-pos); | |||||
MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_ONLY, pos, bufferSize); | |||||
checksum.update( buffer ); | |||||
buffer.rewind(); | |||||
fileSize = channel.size(); | |||||
pos += BUFFER_SIZE; | |||||
} | |||||
checksum.finish(); | |||||
} | |||||
} | |||||
public static List<Checksum> initializeChecksums(Path file, List<ChecksumAlgorithm> checksumAlgorithms) throws IOException { | |||||
final List<Checksum> checksums = newChecksums(checksumAlgorithms); | |||||
update(checksums, file); | |||||
return checksums; | |||||
} | |||||
/** | |||||
* Returns the list of configured checksum types. | |||||
* | |||||
* @param checksumTypes The list of checksum strings | |||||
* @return The list of checksum objects | |||||
*/ | |||||
public static List<ChecksumAlgorithm> getAlgorithms(List<String> checksumTypes) { | |||||
return checksumTypes.stream().map(ca -> | |||||
ChecksumAlgorithm.valueOf(ca.toUpperCase())).collect(Collectors.toList()); | |||||
} | |||||
public static List<Checksum> newChecksums(List<ChecksumAlgorithm> checksumAlgorithms) { | |||||
return checksumAlgorithms.stream().map( a -> new Checksum(a)).collect(Collectors.toList()); | |||||
} | |||||
} |
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import javax.xml.bind.ValidationException; | |||||
import java.io.FileNotFoundException; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.nio.charset.Charset; | import java.nio.charset.Charset; | ||||
import java.nio.file.Files; | import java.nio.file.Files; | ||||
{ | { | ||||
Checksum checksum = new Checksum( checksumAlgorithm ); | Checksum checksum = new Checksum( checksumAlgorithm ); | ||||
checksum.update( referenceFile ); | |||||
ChecksumUtil.update(checksum, referenceFile ); | |||||
return checksum.getChecksum( ); | return checksum.getChecksum( ); | ||||
} | } | ||||
/** | /** | ||||
* Creates a checksum file of the provided referenceFile. | |||||
* Writes a checksum file for the referenceFile. | |||||
* | * | ||||
* @param checksumAlgorithm the hash to use. | * @param checksumAlgorithm the hash to use. | ||||
* @return the checksum File that was created. | * @return the checksum File that was created. | ||||
* @throws IOException if there was a problem either reading the referenceFile, or writing the checksum file. | * @throws IOException if there was a problem either reading the referenceFile, or writing the checksum file. | ||||
*/ | */ | ||||
public Path createChecksum( ChecksumAlgorithm checksumAlgorithm ) | |||||
public Path writeFile(ChecksumAlgorithm checksumAlgorithm ) | |||||
throws IOException | throws IOException | ||||
{ | { | ||||
Path checksumFile = referenceFile.resolveSibling( referenceFile.getFileName( ) + "." + checksumAlgorithm.getExt( ).get( 0 ) ); | |||||
Path checksumFile = referenceFile.resolveSibling( referenceFile.getFileName( ) + "." + checksumAlgorithm.getDefaultExtension() ); | |||||
Files.deleteIfExists( checksumFile ); | Files.deleteIfExists( checksumFile ); | ||||
String checksum = calculateChecksum( checksumAlgorithm ); | String checksum = calculateChecksum( checksumAlgorithm ); | ||||
Files.write( checksumFile, // | Files.write( checksumFile, // | ||||
/** | /** | ||||
* Get the checksum file for the reference file and hash. | * Get the checksum file for the reference file and hash. | ||||
* It returns a file for the given checksum, if one exists with one of the possible extensions. | |||||
* If it does not exist, a default path will be returned. | |||||
* | * | ||||
* @param checksumAlgorithm the hash that we are interested in. | * @param checksumAlgorithm the hash that we are interested in. | ||||
* @return the checksum file to return | * @return the checksum file to return | ||||
return file; | return file; | ||||
} | } | ||||
} | } | ||||
return referenceFile.resolveSibling( referenceFile.getFileName( ) + "." + checksumAlgorithm.getExt( ).get( 0 ) ); | |||||
return referenceFile.resolveSibling( referenceFile.getFileName( ) + "." + checksumAlgorithm.getDefaultExtension() ); | |||||
} | } | ||||
/** | /** | ||||
} | } | ||||
/** | /** | ||||
* Checks if the checksums are valid for the referenced file. | |||||
* Checks if the checksum files are valid for the referenced file. | |||||
* It tries to find a checksum file for each algorithm in the same directory as the referenceFile. | |||||
* The method returns true, if at least one checksum file exists for one of the given algorithms | |||||
* and all existing checksum files are valid. | |||||
* | |||||
* This method throws only exceptions, if throwExceptions is true. Otherwise false will be returned instead. | * This method throws only exceptions, if throwExceptions is true. Otherwise false will be returned instead. | ||||
* | |||||
* It verifies only the existing checksum files. If the checksum file for a particular algorithm does not exist, | |||||
* but others exist and are valid, it will return true. | |||||
* | |||||
* @param algorithms The algorithms to verify | * @param algorithms The algorithms to verify | ||||
* @param throwExceptions If true, exceptions will be thrown, otherwise false will be returned, if a exception occurred. | * @param throwExceptions If true, exceptions will be thrown, otherwise false will be returned, if a exception occurred. | ||||
* @return True, if it is valid, otherwise false. | |||||
* @return True, if it is valid for all existing checksum files, otherwise false. | |||||
* @throws ChecksumValidationException | * @throws ChecksumValidationException | ||||
*/ | */ | ||||
public boolean isValidChecksums( List<ChecksumAlgorithm> algorithms, boolean throwExceptions) throws ChecksumValidationException | public boolean isValidChecksums( List<ChecksumAlgorithm> algorithms, boolean throwExceptions) throws ChecksumValidationException | ||||
{ | { | ||||
List<Checksum> checksums = new ArrayList<>( algorithms.size() ); | |||||
// Create checksum object for each algorithm. | |||||
for ( ChecksumAlgorithm checksumAlgorithm : algorithms ) | |||||
{ | |||||
Path checksumFile = getChecksumFile( checksumAlgorithm ); | |||||
// Only add algorithm if checksum file exists. | |||||
if ( Files.exists( checksumFile ) ) | |||||
{ | |||||
checksums.add( new Checksum( checksumAlgorithm ) ); | |||||
} | |||||
} | |||||
// Any checksums? | |||||
if ( checksums.isEmpty( ) ) | |||||
{ | |||||
// No checksum objects, no checksum files, default to is invalid. | |||||
return false; | |||||
} | |||||
List<Checksum> checksums; | |||||
// Parse file once, for all checksums. | // Parse file once, for all checksums. | ||||
try | try | ||||
{ | { | ||||
Checksum.update( checksums, referenceFile ); | |||||
checksums = ChecksumUtil.initializeChecksums( referenceFile, algorithms ); | |||||
} | } | ||||
catch ( ChecksumValidationException e ) | |||||
catch (IOException e ) | |||||
{ | { | ||||
log.warn( "Unable to update checksum:{}", e.getMessage( ) ); | log.warn( "Unable to update checksum:{}", e.getMessage( ) ); | ||||
if (throwExceptions) { | if (throwExceptions) { | ||||
throw e; | |||||
if (e instanceof FileNotFoundException) { | |||||
throw new ChecksumValidationException(ChecksumValidationException.ValidationError.FILE_NOT_FOUND, e); | |||||
} else { | |||||
throw new ChecksumValidationException(ChecksumValidationException.ValidationError.READ_ERROR, e); | |||||
} | |||||
} else { | } else { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
boolean valid = true; | boolean valid = true; | ||||
boolean fileExists = false; | |||||
// No file exists -> return false | |||||
// if at least one file exists: | |||||
// -> all existing files must be valid | |||||
// check the checksum files | // check the checksum files | ||||
try | try | ||||
{ | { | ||||
for ( Checksum checksum : checksums ) | for ( Checksum checksum : checksums ) | ||||
{ | { | ||||
ChecksumAlgorithm checksumAlgorithm = checksum.getAlgorithm( ); | ChecksumAlgorithm checksumAlgorithm = checksum.getAlgorithm( ); | ||||
Path checksumFile = getChecksumFile( checksumAlgorithm ); | Path checksumFile = getChecksumFile( checksumAlgorithm ); | ||||
String expectedChecksum = parseChecksum( checksumFile, checksumAlgorithm, referenceFile.getFileName( ).toString( ), FILE_ENCODING ); | |||||
if (Files.exists(checksumFile)) { | |||||
fileExists = true; | |||||
String expectedChecksum = parseChecksum(checksumFile, checksumAlgorithm, referenceFile.getFileName().toString(), FILE_ENCODING); | |||||
if ( !checksum.compare( expectedChecksum ) ) | |||||
{ | |||||
valid = false; | |||||
valid &= checksum.compare(expectedChecksum); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return valid; | |||||
return fileExists && valid; | |||||
} | } | ||||
public Path getReferenceFile( ) | public Path getReferenceFile( ) | ||||
public boolean fixChecksum(ChecksumAlgorithm algorithm) { | |||||
public UpdateStatusList fixChecksum(ChecksumAlgorithm algorithm) { | |||||
return fixChecksums( Arrays.asList(algorithm) ); | return fixChecksums( Arrays.asList(algorithm) ); | ||||
} | } | ||||
/** | /** | ||||
* Fix or create checksum files for the reference file. | |||||
* Writes a checksum file, if it does not exist or if it exists and has a different | |||||
* checksum value. | |||||
* | * | ||||
* @param algorithms the hashes to check for. | * @param algorithms the hashes to check for. | ||||
* @return true if checksums were created successfully. | * @return true if checksums were created successfully. | ||||
*/ | */ | ||||
public boolean fixChecksums( List<ChecksumAlgorithm> algorithms ) | |||||
public UpdateStatusList fixChecksums( List<ChecksumAlgorithm> algorithms ) | |||||
{ | { | ||||
List<Checksum> checksums = new ArrayList<>( algorithms.size() ); | |||||
// Create checksum object for each algorithm. | |||||
for ( ChecksumAlgorithm checksumAlgorithm : algorithms ) | |||||
{ | |||||
checksums.add( new Checksum( checksumAlgorithm ) ); | |||||
} | |||||
UpdateStatusList result = UpdateStatusList.INITIALIZE(algorithms); | |||||
List<Checksum> checksums; | |||||
// Any checksums? | |||||
if ( checksums.isEmpty( ) ) | |||||
{ | |||||
// No checksum objects, no checksum files, default to is valid. | |||||
return true; | |||||
} | |||||
try | try | ||||
{ | { | ||||
// Parse file once, for all checksums. | // Parse file once, for all checksums. | ||||
Checksum.update( checksums, referenceFile ); | |||||
checksums = ChecksumUtil.initializeChecksums(getReferenceFile(), algorithms); | |||||
} | } | ||||
catch ( ChecksumValidationException e ) | |||||
catch (IOException e ) | |||||
{ | { | ||||
log.warn( e.getMessage( ), e ); | log.warn( e.getMessage( ), e ); | ||||
return false; | |||||
result.setTotalError(e); | |||||
return result; | |||||
} | |||||
// Any checksums? | |||||
if ( checksums.isEmpty( ) ) | |||||
{ | |||||
// No checksum objects, no checksum files, default to is valid. | |||||
return result; | |||||
} | } | ||||
boolean valid = true; | boolean valid = true; | ||||
if ( !checksum.compare( expectedChecksum ) ) | if ( !checksum.compare( expectedChecksum ) ) | ||||
{ | { | ||||
// create checksum (again) | |||||
// overwrite checksum file | |||||
writeChecksumFile( checksumFile, FILE_ENCODING, checksum.getChecksum( ) ); | writeChecksumFile( checksumFile, FILE_ENCODING, checksum.getChecksum( ) ); | ||||
result.setStatus(checksumAlgorithm,UpdateStatus.UPDATED); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
writeChecksumFile( checksumFile, FILE_ENCODING, checksum.getChecksum( ) ); | writeChecksumFile( checksumFile, FILE_ENCODING, checksum.getChecksum( ) ); | ||||
result.setStatus(checksumAlgorithm, UpdateStatus.CREATED); | |||||
} | } | ||||
} | } | ||||
catch ( ChecksumValidationException e ) | catch ( ChecksumValidationException e ) | ||||
{ | { | ||||
log.warn( e.getMessage( ), e ); | log.warn( e.getMessage( ), e ); | ||||
valid = false; | |||||
result.setErrorStatus(checksumAlgorithm, e); | |||||
} | } | ||||
} | } | ||||
return valid; | |||||
return result; | |||||
} | } | ||||
* </p> | * </p> | ||||
* | * | ||||
* @param checksumFile The file where the checksum is stored | * @param checksumFile The file where the checksum is stored | ||||
* @param expectedHash The checksum algorithm to check | |||||
* @param expectedPath The filename of the reference file | |||||
* @param checksumAlgorithm The checksum algorithm to check | |||||
* @param fileName The filename of the reference file | |||||
* @return | * @return | ||||
* @throws IOException | * @throws IOException | ||||
*/ | */ | ||||
public String parseChecksum( Path checksumFile, ChecksumAlgorithm expectedHash, String expectedPath, Charset encoding ) | |||||
public String parseChecksum( Path checksumFile, ChecksumAlgorithm checksumAlgorithm, String fileName, Charset encoding ) | |||||
throws ChecksumValidationException | throws ChecksumValidationException | ||||
{ | { | ||||
ChecksumFileContent fc = parseChecksumFile( checksumFile, expectedHash, encoding ); | |||||
if ( fc.isFormatMatch() && !isValidChecksumPattern( fc.getFileReference( ), expectedPath ) ) | |||||
ChecksumFileContent fc = parseChecksumFile( checksumFile, checksumAlgorithm, encoding ); | |||||
if ( fc.isFormatMatch() && !isValidChecksumPattern( fc.getFileReference( ), fileName ) ) | |||||
{ | { | ||||
throw new ChecksumValidationException(BAD_CHECKSUM_FILE_REF, | throw new ChecksumValidationException(BAD_CHECKSUM_FILE_REF, | ||||
"The file reference '" + fc.getFileReference( ) + "' in the checksum file does not match expected file: '" + expectedPath + "'" ); | |||||
"The file reference '" + fc.getFileReference( ) + "' in the checksum file does not match expected file: '" + fileName + "'" ); | |||||
} else if (!fc.isFormatMatch()) { | } else if (!fc.isFormatMatch()) { | ||||
throw new ChecksumValidationException( BAD_CHECKSUM_FILE, "The checksum file content could not be parsed: "+checksumFile ); | throw new ChecksumValidationException( BAD_CHECKSUM_FILE, "The checksum file content could not be parsed: "+checksumFile ); | ||||
} | } | ||||
return fc.getChecksum( ); | return fc.getChecksum( ); | ||||
} | |||||
public ChecksumFileContent parseChecksumFile( Path checksumFile, ChecksumAlgorithm expectedHash, Charset encoding ) | |||||
} | |||||
public ChecksumFileContent parseChecksumFile( Path checksumFile, ChecksumAlgorithm checksumAlgorithm, Charset encoding ) | |||||
{ | { | ||||
ChecksumFileContent fc = new ChecksumFileContent( ); | ChecksumFileContent fc = new ChecksumFileContent( ); | ||||
String rawChecksumString = FileUtils.readFileToString( checksumFile, encoding ); | String rawChecksumString = FileUtils.readFileToString( checksumFile, encoding ); | ||||
String trimmedChecksum = rawChecksumString.replace( '\n', ' ' ).trim( ); | String trimmedChecksum = rawChecksumString.replace( '\n', ' ' ).trim( ); | ||||
// Free-BSD / openssl | // Free-BSD / openssl | ||||
String regex = expectedHash.getType( ) + "\\s*\\(([^)]*)\\)\\s*=\\s*([a-fA-F0-9]+)"; | |||||
String regex = checksumAlgorithm.getType( ) + "\\s*\\(([^)]*)\\)\\s*=\\s*([a-fA-F0-9]+)"; | |||||
Matcher m = Pattern.compile( regex ).matcher( trimmedChecksum ); | Matcher m = Pattern.compile( regex ).matcher( trimmedChecksum ); | ||||
if ( m.matches( ) ) | if ( m.matches( ) ) | ||||
{ | { |
package org.apache.archiva.checksum; | |||||
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
/** | |||||
* Status of checksum update for specific algorithm. | |||||
*/ | |||||
public class UpdateStatus { | |||||
/** | |||||
* Checksum file did not exist before and was created | |||||
*/ | |||||
public static final int CREATED = 1; | |||||
/** | |||||
* Checksum file existed, but content differed | |||||
*/ | |||||
public static final int UPDATED = 2; | |||||
/** | |||||
* Nothing changed | |||||
*/ | |||||
public static final int NONE = 0; | |||||
/** | |||||
* Error occured during update/creation of the checksum file | |||||
*/ | |||||
public static final int ERROR = -1; | |||||
private final ChecksumAlgorithm algorithm; | |||||
private final int status; | |||||
private final Throwable error; | |||||
public UpdateStatus(ChecksumAlgorithm algorithm) { | |||||
this.algorithm = algorithm; | |||||
status = NONE; | |||||
error = null; | |||||
} | |||||
public UpdateStatus(ChecksumAlgorithm algorithm, int status) { | |||||
this.algorithm = algorithm; | |||||
this.status = status; | |||||
error = null; | |||||
} | |||||
public UpdateStatus(ChecksumAlgorithm algorithm, Throwable error) { | |||||
this.algorithm = algorithm; | |||||
this.status = ERROR; | |||||
this.error = error; | |||||
} | |||||
/** | |||||
* Return the status value. | |||||
* @return The value | |||||
*/ | |||||
public int getValue() { | |||||
return status; | |||||
} | |||||
/** | |||||
* Return error, if exists, otherwise <code>null</code> will be returned. | |||||
* @return | |||||
*/ | |||||
public Throwable getError() { | |||||
return error; | |||||
} | |||||
/** | |||||
* Return the algorithm, this status is assigned to. | |||||
* @return The checksum algorithm | |||||
*/ | |||||
public ChecksumAlgorithm getAlgorithm() { | |||||
return algorithm; | |||||
} | |||||
} |
package org.apache.archiva.checksum; | |||||
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one | |||||
* or more contributor license agreements. See the NOTICE file | |||||
* distributed with this work for additional information | |||||
* regarding copyright ownership. The ASF licenses this file | |||||
* to you under the Apache License, Version 2.0 (the | |||||
* "License"); you may not use this file except in compliance | |||||
* with the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, | |||||
* software distributed under the License is distributed on an | |||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |||||
* KIND, either express or implied. See the License for the | |||||
* specific language governing permissions and limitations | |||||
* under the License. | |||||
*/ | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.TreeMap; | |||||
/** | |||||
* Container for a list of update status objects. | |||||
* | |||||
* If there is a overall error that is not specific to a algorithm, the total status | |||||
* flag is set to error. | |||||
*/ | |||||
public class UpdateStatusList { | |||||
private int totalStatus = UpdateStatus.NONE; | |||||
private Throwable error; | |||||
private Map<ChecksumAlgorithm, UpdateStatus> statusList = new TreeMap<>(); | |||||
public UpdateStatusList() { | |||||
} | |||||
public void addStatus(UpdateStatus status) { | |||||
statusList.put(status.getAlgorithm(), status); | |||||
} | |||||
public static UpdateStatusList INITIALIZE(List<ChecksumAlgorithm> algorithms) { | |||||
final UpdateStatusList list = new UpdateStatusList(); | |||||
for(ChecksumAlgorithm algorithm : algorithms) { | |||||
list.addStatus(new UpdateStatus(algorithm)); | |||||
} | |||||
return list; | |||||
} | |||||
public int getTotalStatus() { | |||||
return totalStatus; | |||||
} | |||||
public void setTotalError(Throwable e) { | |||||
this.error = e; | |||||
this.totalStatus = UpdateStatus.ERROR; | |||||
} | |||||
public Throwable getTotalError() { | |||||
return error; | |||||
} | |||||
public List<UpdateStatus> getStatusList() { | |||||
return new ArrayList(statusList.values()); | |||||
} | |||||
public void setStatus(ChecksumAlgorithm algorithm, UpdateStatus status) { | |||||
statusList.put(algorithm, status); | |||||
} | |||||
public void setStatus(ChecksumAlgorithm algorithm, int status) { | |||||
statusList.put(algorithm, new UpdateStatus(algorithm, status)); | |||||
} | |||||
public void setErrorStatus(ChecksumAlgorithm algorithm, Throwable e) { | |||||
statusList.put(algorithm, new UpdateStatus(algorithm,e)); | |||||
} | |||||
public UpdateStatus getStatus(ChecksumAlgorithm algorithm) { | |||||
return statusList.get(algorithm); | |||||
} | |||||
} |
* under the License. | * under the License. | ||||
*/ | */ | ||||
import junit.framework.TestCase; | |||||
import org.apache.archiva.common.utils.FileUtils; | import org.apache.archiva.common.utils.FileUtils; | ||||
import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner; | import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.runner.RunWith; | import org.junit.runner.RunWith; | ||||
import java.io.ByteArrayInputStream; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.nio.ByteBuffer; | |||||
import java.nio.charset.Charset; | import java.nio.charset.Charset; | ||||
import java.nio.file.Path; | import java.nio.file.Path; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
FileUtils.writeStringToFile( checkFile, FILE_ENCODING, "You know, I'm sick of following my dreams, man. " | FileUtils.writeStringToFile( checkFile, FILE_ENCODING, "You know, I'm sick of following my dreams, man. " | ||||
+ "I'm just going to ask where they're going and hook up with 'em later. - Mitch Hedberg"); | + "I'm just going to ask where they're going and hook up with 'em later. - Mitch Hedberg"); | ||||
Checksum.update( checksums, checkFile ); | |||||
ChecksumUtil.update( checksums, checkFile ); | |||||
assertEquals( "Checksum SHA1", "e396119ae0542e85a74759602fd2f81e5d36d762", checksumSha1.getChecksum() ); | assertEquals( "Checksum SHA1", "e396119ae0542e85a74759602fd2f81e5d36d762", checksumSha1.getChecksum() ); | ||||
assertEquals( "Checksum MD5", "21c2c5ca87ec018adacb2e2fb3432219", checksumMd5.getChecksum() ); | assertEquals( "Checksum MD5", "21c2c5ca87ec018adacb2e2fb3432219", checksumMd5.getChecksum() ); |
{ | { | ||||
Path testableJar = createTestableJar( "examples/redback-authz-open.jar" ); | Path testableJar = createTestableJar( "examples/redback-authz-open.jar" ); | ||||
ChecksummedFile checksummedFile = new ChecksummedFile( testableJar ); | ChecksummedFile checksummedFile = new ChecksummedFile( testableJar ); | ||||
checksummedFile.createChecksum( ChecksumAlgorithm.SHA1 ); | |||||
checksummedFile.writeFile( ChecksumAlgorithm.SHA1 ); | |||||
Path hashFile = checksummedFile.getChecksumFile( ChecksumAlgorithm.SHA1 ); | Path hashFile = checksummedFile.getChecksumFile( ChecksumAlgorithm.SHA1 ); | ||||
assertTrue( "ChecksumAlgorithm file should exist.", Files.exists(hashFile) ); | assertTrue( "ChecksumAlgorithm file should exist.", Files.exists(hashFile) ); | ||||
String hashContents = org.apache.commons.io.FileUtils.readFileToString( hashFile.toFile(), "UTF-8" ); | String hashContents = org.apache.commons.io.FileUtils.readFileToString( hashFile.toFile(), "UTF-8" ); | ||||
assertFalse( "ChecksummedFile.isValid(SHA1) == false", | assertFalse( "ChecksummedFile.isValid(SHA1) == false", | ||||
checksummedFile.isValidChecksum( ChecksumAlgorithm.SHA1 ) ); | checksummedFile.isValidChecksum( ChecksumAlgorithm.SHA1 ) ); | ||||
boolean fixed = checksummedFile.fixChecksums( Arrays.asList( ChecksumAlgorithm.SHA1 ) ); | |||||
assertTrue( "ChecksummedFile.fixChecksums() == true", fixed ); | |||||
UpdateStatusList fixed = checksummedFile.fixChecksums( Arrays.asList( ChecksumAlgorithm.SHA1 ) ); | |||||
assertEquals(1, fixed.getStatusList().size()); | |||||
assertFalse(fixed.getTotalStatus()==UpdateStatus.ERROR); | |||||
assertTrue( "ChecksummedFile.fixChecksums() == true", fixed.getStatusList().get(0).getValue()==UpdateStatus.UPDATED ); | |||||
assertTrue( "ChecksummedFile.isValid(SHA1) == true", | assertTrue( "ChecksummedFile.isValid(SHA1) == true", | ||||
checksummedFile.isValidChecksum( ChecksumAlgorithm.SHA1 ) ); | checksummedFile.isValidChecksum( ChecksumAlgorithm.SHA1 ) ); |
* under the License. | * under the License. | ||||
*/ | */ | ||||
import java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
/** | /** | ||||
* | * | ||||
* The runtime configuration. | * The runtime configuration. | ||||
*/ | */ | ||||
private String languageRange = "en,fr,de"; | private String languageRange = "en,fr,de"; | ||||
/** | |||||
* List of checksum types (algorithms) that should be applied to repository artifacts. | |||||
*/ | |||||
private List<String> checksumTypes = new ArrayList(Arrays.asList("MD5","SHA1","SHA256")); | |||||
//-----------/ | //-----------/ | ||||
//- Methods -/ | //- Methods -/ | ||||
this.urlFailureCacheConfiguration = urlFailureCacheConfiguration; | this.urlFailureCacheConfiguration = urlFailureCacheConfiguration; | ||||
} //-- void setUrlFailureCacheConfiguration( CacheConfiguration ) | } //-- void setUrlFailureCacheConfiguration( CacheConfiguration ) | ||||
/** | |||||
* Returns the list of checksum types to generate | |||||
* @return | |||||
*/ | |||||
public List<String> getChecksumTypes() | |||||
{ | |||||
if ( this.checksumTypes == null ) | |||||
{ | |||||
this.checksumTypes = new java.util.ArrayList<String>(); | |||||
} | |||||
return this.checksumTypes; | |||||
} | |||||
/** | |||||
* Adds a checksum type | |||||
* @param type | |||||
*/ | |||||
public void addChecksumType(String type) { | |||||
if (!getChecksumTypes().contains(type)) { | |||||
getChecksumTypes().add(type); | |||||
} | |||||
} | |||||
/** | |||||
* Removes a checksum type | |||||
* @param type | |||||
*/ | |||||
public void removeChecksumType(String type) { | |||||
getChecksumTypes().remove(type); | |||||
} | |||||
/** | |||||
* Set all checksum types | |||||
* @param checksumTypes | |||||
*/ | |||||
public void setChecksumTypes(List<String> checksumTypes) { | |||||
if (checksumTypes!=null) { | |||||
getChecksumTypes().clear(); | |||||
getChecksumTypes().addAll(checksumTypes); | |||||
} | |||||
} | |||||
} | } |
value.setLanguageRange(languageRange); | value.setLanguageRange(languageRange); | ||||
List<String> checksumTypeList = registry.getList(prefix + "checksumTypes.type"); | |||||
value.setChecksumTypes(checksumTypeList); | |||||
return value; | return value; | ||||
} | } | ||||
import org.apache.archiva.redback.components.registry.Registry; | import org.apache.archiva.redback.components.registry.Registry; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.List; | |||||
// Util imports | // Util imports | ||||
// Model class imports | // Model class imports | ||||
writeConfiguration("", model, registry); | writeConfiguration("", model, registry); | ||||
} | } | ||||
private void writeList(Registry registry, List<String> subList, String subsetPath, String elementName) { | |||||
if (subList != null && subList.size() > 0 | |||||
) { | |||||
registry.removeSubset(subsetPath); | |||||
int count = 0; | |||||
for (Iterator<String> iter = subList.iterator(); iter.hasNext(); count++) { | |||||
String name = subsetPath + "." + elementName + "(" + count + ")"; | |||||
String value = iter.next(); | |||||
registry.setString(name, value); | |||||
} | |||||
} | |||||
} | |||||
private void writeConfiguration(String prefix, Configuration value, Registry registry) { | private void writeConfiguration(String prefix, Configuration value, Registry registry) { | ||||
if (value != null) { | if (value != null) { | ||||
if (value.getVersion() != null | if (value.getVersion() != null | ||||
String languageRange = "languageRange"; | String languageRange = "languageRange"; | ||||
registry.setString(prefix + languageRange, value.getLanguageRange()); | registry.setString(prefix + languageRange, value.getLanguageRange()); | ||||
} | } | ||||
writeList(registry, value.getChecksumTypes(), prefix+"checksumTypes", "type"); | |||||
} | } | ||||
} | } | ||||
</ui> | </ui> | ||||
</webapp> | </webapp> | ||||
<archivaRuntimeConfiguration> | |||||
<checksumTypes> | |||||
<type>MD5</type> | |||||
<type>SHA1</type> | |||||
<type>SHA256</type> | |||||
</checksumTypes> | |||||
</archivaRuntimeConfiguration> | |||||
<redbackRuntimeConfiguration> | <redbackRuntimeConfiguration> | ||||
<userManagerImpls> | <userManagerImpls> | ||||
<userManagerImpl>jpa</userManagerImpl> | <userManagerImpl>jpa</userManagerImpl> |
* under the License. | * under the License. | ||||
*/ | */ | ||||
import org.apache.archiva.checksum.ChecksumAlgorithm; | |||||
import org.apache.archiva.checksum.ChecksummedFile; | |||||
import org.apache.archiva.checksum.*; | |||||
import org.apache.archiva.configuration.ArchivaConfiguration; | import org.apache.archiva.configuration.ArchivaConfiguration; | ||||
import org.apache.archiva.configuration.FileTypes; | import org.apache.archiva.configuration.FileTypes; | ||||
import org.apache.archiva.consumers.AbstractMonitoredConsumer; | import org.apache.archiva.consumers.AbstractMonitoredConsumer; | ||||
private String id = "create-missing-checksums"; | private String id = "create-missing-checksums"; | ||||
private String description = "Create Missing and/or Fix Invalid Checksums (.sha1, .md5)"; | |||||
private String description = "Create Missing and/or Fix Invalid Checksum files."; | |||||
private ArchivaConfiguration configuration; | private ArchivaConfiguration configuration; | ||||
private FileTypes filetypes; | private FileTypes filetypes; | ||||
private ChecksummedFile checksum; | |||||
private static final String TYPE_CHECKSUM_NOT_FILE = "checksum-bad-not-file"; | private static final String TYPE_CHECKSUM_NOT_FILE = "checksum-bad-not-file"; | ||||
private static final String TYPE_CHECKSUM_CANNOT_CALC = "checksum-calc-failure"; | private static final String TYPE_CHECKSUM_CANNOT_CALC = "checksum-calc-failure"; | ||||
private Path repositoryDir; | private Path repositoryDir; | ||||
private List<String> includes = new ArrayList<>( 0 ); | private List<String> includes = new ArrayList<>( 0 ); | ||||
private List<ChecksumAlgorithm> algorithms; | |||||
@Inject | @Inject | ||||
public ArtifactMissingChecksumsConsumer( ArchivaConfiguration configuration, FileTypes filetypes ) | public ArtifactMissingChecksumsConsumer( ArchivaConfiguration configuration, FileTypes filetypes ) | ||||
public void processFile( String path ) | public void processFile( String path ) | ||||
throws ConsumerException | throws ConsumerException | ||||
{ | { | ||||
createFixChecksum( path, ChecksumAlgorithm.SHA1 ); | |||||
createFixChecksum( path, ChecksumAlgorithm.MD5 ); | |||||
Path artifactPath = repositoryDir.resolve(path); | |||||
ChecksummedFile csFile = new ChecksummedFile(artifactPath); | |||||
UpdateStatusList result = csFile.fixChecksums(algorithms); | |||||
if (result.getTotalStatus()== UpdateStatus.ERROR) { | |||||
log.warn( "Error accessing file {}. ", path ); | |||||
triggerConsumerWarning( TYPE_CHECKSUM_NOT_FILE, | |||||
"Error accessing file " + path + "." ); | |||||
} else { | |||||
result.getStatusList().stream().forEach(st -> | |||||
triggerInfo(path, st)); | |||||
} | |||||
} | |||||
private void triggerInfo(String path, UpdateStatus status) { | |||||
switch (status.getValue()) { | |||||
case UpdateStatus.ERROR: | |||||
log.error( "Cannot create checksum for file {} :", path, status.getError() ); | |||||
triggerConsumerError( TYPE_CHECKSUM_CANNOT_CREATE, "Cannot create checksum for file " + path + | |||||
": " + status.getError().getMessage( ) ); | |||||
break; | |||||
case UpdateStatus.CREATED: | |||||
log.info( "Created missing checksum file {}", path ); | |||||
triggerConsumerInfo( "Created missing checksum file " + path ); | |||||
break; | |||||
case UpdateStatus.UPDATED: | |||||
log.info( "Fixed checksum file {}", path ); | |||||
triggerConsumerInfo( "Fixed checksum file " + path ); | |||||
break; | |||||
} | |||||
} | } | ||||
@Override | @Override | ||||
processFile( path ); | processFile( path ); | ||||
} | } | ||||
private void createFixChecksum( String path, ChecksumAlgorithm checksumAlgorithm ) | |||||
{ | |||||
Path artifactFile = repositoryDir.resolve(path); | |||||
Path checksumFile = repositoryDir.resolve(path + "." + checksumAlgorithm.getExt( ).get(0) ); | |||||
if ( Files.exists(checksumFile) ) | |||||
{ | |||||
checksum = new ChecksummedFile( artifactFile); | |||||
if ( !checksum.isValidChecksum( checksumAlgorithm ) ) | |||||
{ | |||||
checksum.fixChecksum( checksumAlgorithm ); | |||||
log.info( "Fixed checksum file {}", checksumFile.toAbsolutePath( ) ); | |||||
triggerConsumerInfo( "Fixed checksum file " + checksumFile.toAbsolutePath( ) ); | |||||
} | |||||
} | |||||
else if ( !Files.exists(checksumFile) ) | |||||
{ | |||||
checksum = new ChecksummedFile( artifactFile); | |||||
try | |||||
{ | |||||
checksum.createChecksum( checksumAlgorithm ); | |||||
log.info( "Created missing checksum file {}", checksumFile.toAbsolutePath( ) ); | |||||
triggerConsumerInfo( "Created missing checksum file " + checksumFile.toAbsolutePath( ) ); | |||||
} | |||||
catch ( IOException e ) | |||||
{ | |||||
log.error( "Cannot create checksum for file {} :", checksumFile, e ); | |||||
triggerConsumerError( TYPE_CHECKSUM_CANNOT_CREATE, "Cannot create checksum for file " + checksumFile + | |||||
": " + e.getMessage( ) ); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
log.warn( "Checksum file {} is not a file. ", checksumFile.toAbsolutePath( ) ); | |||||
triggerConsumerWarning( TYPE_CHECKSUM_NOT_FILE, | |||||
"Checksum file " + checksumFile.toAbsolutePath( ) + " is not a file." ); | |||||
} | |||||
} | |||||
/* | /* | ||||
@Override | @Override | ||||
//configuration.addChangeListener( this ); | //configuration.addChangeListener( this ); | ||||
initIncludes( ); | initIncludes( ); | ||||
algorithms = ChecksumUtil.getAlgorithms(configuration.getConfiguration().getArchivaRuntimeConfiguration().getChecksumTypes()); | |||||
} | } | ||||
} | } |
@PostConstruct | @PostConstruct | ||||
public void initialize( ) | public void initialize( ) | ||||
{ | { | ||||
Set<String> extensions = ChecksumAlgorithm.getExtensions(); | |||||
Set<String> extensions = ChecksumAlgorithm.getAllExtensions(); | |||||
includes = new ArrayList<>( extensions.size() ); | includes = new ArrayList<>( extensions.size() ); | ||||
for ( String ext : extensions ) | for ( String ext : extensions ) | ||||
{ | { |
(FileType) archivaConfiguration.getConfiguration().getRepositoryScanning().getFileTypes().get( 0 ); | (FileType) archivaConfiguration.getConfiguration().getRepositoryScanning().getFileTypes().get( 0 ); | ||||
assertEquals( FileTypes.ARTIFACTS, fileType.getId() ); | assertEquals( FileTypes.ARTIFACTS, fileType.getId() ); | ||||
fileType.addPattern( "**/*.xml" ); | fileType.addPattern( "**/*.xml" ); | ||||
archivaConfiguration.getConfiguration().getArchivaRuntimeConfiguration().addChecksumType("MD5"); | |||||
archivaConfiguration.getConfiguration().getArchivaRuntimeConfiguration().addChecksumType("SHA1"); | |||||
archivaConfiguration.getConfiguration().getArchivaRuntimeConfiguration().addChecksumType("SHA256"); | |||||
repoLocation = Paths.get( "target/test-" + getName() + "/test-repo" ); | repoLocation = Paths.get( "target/test-" + getName() + "/test-repo" ); | ||||
} | } |
import org.apache.archiva.checksum.ChecksumAlgorithm; | import org.apache.archiva.checksum.ChecksumAlgorithm; | ||||
import org.apache.archiva.checksum.ChecksummedFile; | import org.apache.archiva.checksum.ChecksummedFile; | ||||
import org.apache.archiva.checksum.UpdateStatus; | |||||
import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
if ( FIX.equals( policySetting ) ) | if ( FIX.equals( policySetting ) ) | ||||
{ | { | ||||
ChecksummedFile checksum = new ChecksummedFile( localFile ); | ChecksummedFile checksum = new ChecksummedFile( localFile ); | ||||
if ( checksum.fixChecksums( algorithms ) ) | |||||
if ( checksum.fixChecksums( algorithms ).getTotalStatus() != UpdateStatus.ERROR ) | |||||
{ | { | ||||
log.debug( "Checksum policy set to FIX, checksum files have been updated." ); | log.debug( "Checksum policy set to FIX, checksum files have been updated." ); | ||||
return; | return; |
import org.apache.archiva.admin.model.beans.ManagedRepository; | import org.apache.archiva.admin.model.beans.ManagedRepository; | ||||
import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; | import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; | ||||
import org.apache.archiva.checksum.ChecksumAlgorithm; | import org.apache.archiva.checksum.ChecksumAlgorithm; | ||||
import org.apache.archiva.checksum.ChecksumUtil; | |||||
import org.apache.archiva.checksum.ChecksummedFile; | import org.apache.archiva.checksum.ChecksummedFile; | ||||
import org.apache.archiva.common.utils.VersionComparator; | import org.apache.archiva.common.utils.VersionComparator; | ||||
import org.apache.archiva.common.utils.VersionUtil; | import org.apache.archiva.common.utils.VersionUtil; | ||||
import org.apache.archiva.configuration.ArchivaConfiguration; | |||||
import org.apache.archiva.maven2.metadata.MavenMetadataReader; | import org.apache.archiva.maven2.metadata.MavenMetadataReader; | ||||
import org.apache.archiva.metadata.model.facets.AuditEvent; | import org.apache.archiva.metadata.model.facets.AuditEvent; | ||||
import org.apache.archiva.model.ArchivaRepositoryMetadata; | import org.apache.archiva.model.ArchivaRepositoryMetadata; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
import javax.annotation.PostConstruct; | |||||
import javax.inject.Inject; | import javax.inject.Inject; | ||||
import javax.inject.Named; | import javax.inject.Named; | ||||
import javax.servlet.http.HttpServletRequest; | import javax.servlet.http.HttpServletRequest; | ||||
import java.text.SimpleDateFormat; | import java.text.SimpleDateFormat; | ||||
import java.util.*; | import java.util.*; | ||||
import java.util.concurrent.CopyOnWriteArrayList; | import java.util.concurrent.CopyOnWriteArrayList; | ||||
import java.util.stream.Collectors; | |||||
/** | /** | ||||
* | |||||
* Service for uploading files to the repository. | |||||
* | |||||
* @author Olivier Lamy | * @author Olivier Lamy | ||||
* @author Martin Stockhammer | |||||
*/ | */ | ||||
@Service("fileUploadService#rest") | @Service("fileUploadService#rest") | ||||
public class DefaultFileUploadService | public class DefaultFileUploadService | ||||
@Inject | @Inject | ||||
private ArchivaAdministration archivaAdministration; | private ArchivaAdministration archivaAdministration; | ||||
private List<ChecksumAlgorithm> algorithms = Arrays.asList(ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5); | |||||
@Inject | |||||
ArchivaConfiguration configuration; | |||||
private List<ChecksumAlgorithm> algorithms; | |||||
private final String FS = FileSystems.getDefault().getSeparator(); | private final String FS = FileSystems.getDefault().getSeparator(); | ||||
StringUtils.trim(URLDecoder.decode(IOUtils.toString(attachment.getDataHandler().getInputStream(), "UTF-8"), "UTF-8")); | StringUtils.trim(URLDecoder.decode(IOUtils.toString(attachment.getDataHandler().getInputStream(), "UTF-8"), "UTF-8")); | ||||
} | } | ||||
@PostConstruct | |||||
private void initialize() { | |||||
algorithms = ChecksumUtil.getAlgorithms(configuration.getConfiguration().getArchivaRuntimeConfiguration().getChecksumTypes()); | |||||
} | |||||
@Override | @Override | ||||
public FileMetadata post(MultipartBody multipartBody) | public FileMetadata post(MultipartBody multipartBody) | ||||
throws ArchivaRestServiceException { | throws ArchivaRestServiceException { | ||||
@Override | @Override | ||||
public List<FileMetadata> getSessionFileMetadatas() | public List<FileMetadata> getSessionFileMetadatas() | ||||
throws ArchivaRestServiceException { | throws ArchivaRestServiceException { | ||||
@SuppressWarnings("unchecked") List<FileMetadata> fileMetadatas = | |||||
(List<FileMetadata>) httpServletRequest.getSession().getAttribute(FILES_SESSION_KEY); | |||||
return fileMetadatas == null ? Collections.<FileMetadata>emptyList() : fileMetadatas; | |||||
return getSessionFilesList(); | |||||
} | } | ||||
private boolean hasValidChars(String checkString) { | private boolean hasValidChars(String checkString) { | ||||
if (checkString.contains(FS)) { | if (checkString.contains(FS)) { | ||||
return false; | return false; | ||||
filename = filename.replaceAll(VersionUtil.SNAPSHOT, timestamp + "-" + newBuildNumber); | filename = filename.replaceAll(VersionUtil.SNAPSHOT, timestamp + "-" + newBuildNumber); | ||||
} | } | ||||
boolean fixChecksums = | |||||
!(archivaAdministration.getKnownContentConsumers().contains("create-missing-checksums")); | |||||
// We always fix checksums for newly uploaded files, even if the content consumer is active. | |||||
boolean fixChecksums = true; | |||||
// !(archivaAdministration.getKnownContentConsumers().contains("create-missing-checksums")); | |||||
try { | try { | ||||
Path targetFile = targetPath.resolve(filename); | Path targetFile = targetPath.resolve(filename); |
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; | import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; | ||||
import org.apache.archiva.configuration.ArchivaConfiguration; | import org.apache.archiva.configuration.ArchivaConfiguration; | ||||
import org.apache.archiva.redback.rest.services.AbstractRestServicesTest; | import org.apache.archiva.redback.rest.services.AbstractRestServicesTest; | ||||
import org.apache.archiva.redback.rest.services.FakeCreateAdminService; | |||||
import org.apache.archiva.rest.api.services.ArchivaRestServiceException; | import org.apache.archiva.rest.api.services.ArchivaRestServiceException; | ||||
import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner; | import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner; | ||||
import org.apache.archiva.web.api.FileUploadService; | import org.apache.archiva.web.api.FileUploadService; | ||||
service.post(body); | service.post(body); | ||||
service.save("internal", "org.apache.archiva", "archiva-model", "1.2", "jar", true); | service.save("internal", "org.apache.archiva", "archiva-model", "1.2", "jar", true); | ||||
} | } | ||||
@Test | |||||
public void saveFileWithOtherExtension() throws IOException, ArchivaRestServiceException { | |||||
log.debug("Starting saveFileWithOtherExtension()"); | |||||
Path path = Paths.get("target/appserver-base/repositories/internal/data/repositories/internal/org/apache/archiva/archiva-model/1.2/archiva-model-1.2.bin"); | |||||
log.debug("Jar exists: {}",Files.exists(path)); | |||||
Files.deleteIfExists(path); | |||||
Path pomPath = Paths.get("target/appserver-base/repositories/internal/data/repositories/internal/org/apache/archiva/archiva-model/1.2/archiva-model-1.2.pom"); | |||||
Files.deleteIfExists(pomPath); | |||||
FileUploadService service = getUploadService(); | |||||
service.clearUploadedFiles(); | |||||
Path file = Paths.get("src/test/repositories/snapshot-repo/org/apache/archiva/archiva-model/1.4-M4-SNAPSHOT/archiva-model-1.4-M4-20130425.081822-1.jar"); | |||||
log.debug("Upload file exists: {}", Files.exists(file)); | |||||
final Attachment fileAttachment = new AttachmentBuilder().object(Files.newInputStream(file)).contentDisposition(new ContentDisposition("form-data; filename=\"archiva-model.bin\"; name=\"files[]\"")).build(); | |||||
MultipartBody body = new MultipartBody(fileAttachment); | |||||
service.post(body); | |||||
assertTrue(service.save("internal", "org.apache.archiva", "archiva-model", "1.2", "bin", false)); | |||||
assertTrue(Files.exists(path)); | |||||
} | |||||
} | } |