org.spdx.rdfparser.VerificationCodeGenerator Maven / Gradle / Ivy
/**
* Copyright (c) 2011 Source Auditor Inc.
*
* Licensed 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.
*
*/
package org.spdx.rdfparser;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.spdx.rdfparser.model.SpdxFile;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* Generates a package verification code from a directory of source code or an array of SPDXFile
s.
*
* A class implementing the IFileChecksumGenerator is supplied as a parameter to the constructor.
* The method getFileChecksum
is called for each file in the directory. This can
* be used as a hook to capture all files in the directory and capture the checksum values at
* a file level.
*
* @author Gary O'Neall
*
*/
public class VerificationCodeGenerator {
private IFileChecksumGenerator fileChecksumGenerator;
public VerificationCodeGenerator(IFileChecksumGenerator fileChecksumGenerator) {
this.fileChecksumGenerator = fileChecksumGenerator;
}
/**
* Generate the SPDX Package Verification Code from an array of SPDXFiles
* @param spdxFiles Files to generate the VerificationCode from
* @param skippedFilePaths File path names to not include in the VerificationCode
* @return VerificationCode based on all files in spdxFiles minus the skippedFilePaths
* @throws NoSuchAlgorithmException
*/
public SpdxPackageVerificationCode generatePackageVerificationCode(SpdxFile[] spdxFiles, String[] skippedFilePaths) throws NoSuchAlgorithmException {
if (spdxFiles == null) {
return null;
}
Set skippedFilePathSet = Sets.newTreeSet();
if (skippedFilePaths != null) {
for (int i = 0; i < skippedFilePaths.length; i++) {
if (skippedFilePaths[i] != null) {
skippedFilePathSet.add(skippedFilePaths[i]);
}
}
}
List fileChecksums = Lists.newArrayList();
for (int i = 0; i < spdxFiles.length; i++) {
if (spdxFiles[i] != null && spdxFiles[i].getName() != null &&
!skippedFilePathSet.contains(spdxFiles[i].getName())) {
fileChecksums.add(spdxFiles[i].getSha1());
}
}
return generatePackageVerificationCode(fileChecksums, skippedFilePaths);
}
/**
* Generate the SPDX Package Verification Code from a directory of files included in the archive
* @param sourceDirectory
* @return
* @throws NoSuchAlgorithmException
* @throws IOException
*/
public SpdxPackageVerificationCode generatePackageVerificationCode(File sourceDirectory, File[] skippedFiles) throws NoSuchAlgorithmException, IOException {
// create a sorted list of file paths
Set skippedFilesPath = Sets.newTreeSet();
String rootOfDirectory = sourceDirectory.getAbsolutePath();
int rootLen = rootOfDirectory.length()+1;
for (int i = 0; i < skippedFiles.length; i++) {
String skippedPath = normalizeFilePath(skippedFiles[i].getAbsolutePath().substring(rootLen));
skippedFilesPath.add(skippedPath);
}
List fileChecksums = Lists.newArrayList();
collectFileData(rootOfDirectory, sourceDirectory, fileChecksums, skippedFilesPath);
String[] skippedFileNames = new String[skippedFilesPath.size()];
Iterator iter = skippedFilesPath.iterator();
int i = 0;
while (iter.hasNext()) {
skippedFileNames[i++] = iter.next();
}
return generatePackageVerificationCode(fileChecksums, skippedFileNames);
}
protected SpdxPackageVerificationCode generatePackageVerificationCode(List fileChecksums,
String[] skippedFilePaths) throws NoSuchAlgorithmException {
Collections.sort(fileChecksums);
MessageDigest verificationCodeDigest = MessageDigest.getInstance("SHA-1");
for (int i = 0;i < fileChecksums.size(); i++) {
byte[] hashInput = fileChecksums.get(i).getBytes(Charset.forName("UTF-8"));
verificationCodeDigest.update(hashInput);
}
String value = convertChecksumToString(verificationCodeDigest.digest());
SpdxPackageVerificationCode retval = new SpdxPackageVerificationCode(value, skippedFilePaths);
return retval;
}
/**
* Collect the file level checksums and filenames
* @param prefixForRelative The portion of the filepath which preceeds the relative file path for the archive
* @param sourceDirectory
* @param fileNameAndChecksums
* @throws IOException
*/
private void collectFileData(String prefixForRelative, File sourceDirectory,
List fileNameAndChecksums, Set skippedFiles) throws IOException {
if (!sourceDirectory.isDirectory()) {
return;
}
File[] filesAndDirs = sourceDirectory.listFiles();
if (filesAndDirs == null) {
return;
}
for (int i = 0; i < filesAndDirs.length; i++) {
if (filesAndDirs[i].isDirectory()) {
collectFileData(prefixForRelative, filesAndDirs[i], fileNameAndChecksums, skippedFiles);
} else {
String filePath = normalizeFilePath(filesAndDirs[i].getAbsolutePath()
.substring(prefixForRelative.length()+1));
if (!skippedFiles.contains(filePath)) {
String checksumValue = this.fileChecksumGenerator.getFileChecksum(filesAndDirs[i]).toLowerCase();
fileNameAndChecksums.add(checksumValue);
}
}
}
}
/**
* Normalizes a file path per the SPDX spec
* @param nonNormalizedFilePath
* @return
*/
public static String normalizeFilePath(String nonNormalizedFilePath) {
String filePath = nonNormalizedFilePath.replace('\\', '/').trim();
if (filePath.contains("../")) {
// need to remove these references
String[] filePathParts = filePath.split("/");
StringBuilder normalizedFilePath = new StringBuilder();
for (int j = 0; j < filePathParts.length; j++) {
if (j+1 < filePathParts.length && filePathParts[j+1].equals("..")) {
// skip this directory
} else if (filePathParts[j].equals("..")) {
// remove these from the filePath
} else {
if (j > 0) {
normalizedFilePath.append('/');
}
normalizedFilePath.append(filePathParts[j]);
}
}
filePath = normalizedFilePath.toString();
}
filePath = filePath.replace("./", "");
if (!filePath.isEmpty() && filePath.charAt(0) == '/') {
filePath = "." + filePath;
} else {
filePath = "./" + filePath;
}
return filePath;
}
/**
* Convert a byte array SHA-1 digest into a 40 character hex string
* @param digest
* @return
*/
private static String convertChecksumToString(byte[] digest) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < digest.length; i++) {
String hex = Integer.toHexString(0xff & digest[i]);
if (hex.length() < 2) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
/**
* @param sourceDirectory
* @param skippedFiles
* @return
* @throws NoSuchAlgorithmException
* @throws IOException
*/
public SpdxPackageVerificationCode generatePackageVerificationCode(
File sourceDirectory) throws NoSuchAlgorithmException, IOException {
return generatePackageVerificationCode(sourceDirectory, new File[0]);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy