/* DigiDoc4J library
*
* This software is released under either the GNU Library General Public
* License (see LICENSE.LGPL).
*
* Note that the only valid version of the LGPL license as far as this
* project is concerned is the original GNU Library General Public License
* Version 2.1, February 1999
*/
package org.digidoc4j;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.apache.commons.io.IOUtils;
import org.digidoc4j.exceptions.DigiDoc4JException;
import org.digidoc4j.exceptions.InvalidDataFileException;
import org.digidoc4j.exceptions.TechnicalException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.digidoc4j.impl.StreamDocument;
import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.Digest;
import eu.europa.esig.dss.DigestAlgorithm;
import eu.europa.esig.dss.FileDocument;
import eu.europa.esig.dss.InMemoryDocument;
import eu.europa.esig.dss.MimeType;
/**
* Data file wrapper providing methods for handling signed files or files to be signed in Container.
*/
public class DataFile implements Serializable {
private static final Logger logger = LoggerFactory.getLogger(DataFile.class);
private DSSDocument document = null;
private Digest digest = null;
private String id;
/**
* Creates container.
*
* @param path file name with path
* @param mimeType MIME type of the data file, for example 'text/plain' or 'application/msword'
*/
public DataFile(String path, String mimeType) {
logger.debug("Path: " + path + ", mime type: " + mimeType);
try {
document = new FileDocument(path);
document.setMimeType(getMimeType(mimeType));
} catch (Exception e) {
logger.error(e.getMessage());
throw new InvalidDataFileException(e);
}
}
/**
* Creates in memory document container.
*
* @param data file content
* @param fileName file name with path
* @param mimeType MIME type of the data file, for example 'text/plain' or 'application/msword'
*/
public DataFile(byte[] data, String fileName, String mimeType) {
logger.debug("File name: " + fileName + ", mime type: " + mimeType);
ByteArrayInputStream stream = new ByteArrayInputStream(data);
document = new InMemoryDocument(stream, fileName, getMimeType(mimeType));
IOUtils.closeQuietly(stream);
}
/**
* Creates in memory document container.
*
* @param stream file content from stream
* @param fileName file name with path
* @param mimeType MIME type of the stream file, for example 'text/plain' or 'application/msword'
*/
public DataFile(InputStream stream, String fileName, String mimeType) {
logger.debug("File name: " + fileName + ", mime type: " + mimeType);
try {
document = new InMemoryDocument(stream, fileName, getMimeType(mimeType));
} catch (Exception e) {
logger.error(e.getMessage());
throw new InvalidDataFileException(e);
}
}
protected DataFile(DSSDocument document) {
this.document = document;
}
protected DataFile() {
}
protected MimeType getMimeType(String mimeType) {
try {
MimeType mimeTypeCode = MimeType.fromMimeTypeString(mimeType);
logger.debug("Mime type: ", mimeTypeCode);
return mimeTypeCode;
} catch (DSSException e) {
logger.error(e.getMessage());
throw new InvalidDataFileException(e);
}
}
/**
* Calculates digest http://www.w3.org/2001/04/xmlenc#sha256 for the data file.
* If the digest has already been calculated it will return it, otherwise it calculates the digest.
* <p/>
*
* @return calculated digest
* @throws Exception thrown if the file does not exist or the digest calculation fails.
*/
public byte[] calculateDigest() throws Exception {
logger.debug("");
return calculateDigest(new URL("http://www.w3.org/2001/04/xmlenc#sha256"));
}
/**
* Calculates digest for data file. If digest is already calculated returns it, otherwise calculates the digest.
* <p>Supported uris for BDoc:</p>
* <br>http://www.w3.org/2000/09/xmldsig#sha1
* <br>http://www.w3.org/2001/04/xmldsig-more#sha224
* <br>http://www.w3.org/2001/04/xmlenc#sha256
* <br>http://www.w3.org/2001/04/xmldsig-more#sha384
* <br>http://www.w3.org/2001/04/xmlenc#sha512
* <p>In case of DDoc files the parameter is ignored and SHA1 hash is always returned</p>
*
* @param method method uri for calculating the digest
* @return calculated digest
*/
public byte[] calculateDigest(URL method) { // TODO exceptions to throw
logger.debug("URL method: " + method);
if (digest == null) {
DigestAlgorithm digestAlgorithm = DigestAlgorithm.forXML(method.toString());
digest = new Digest(digestAlgorithm, calculateDigestInternal(digestAlgorithm));
} else {
logger.debug("Returning existing digest value");
}
return digest.getValue();
}
/**
* @param digestType digest algorithm type
* @return digest algorithm uri
*/
public byte[] calculateDigest(org.digidoc4j.DigestAlgorithm digestType) {
logger.debug("");
return calculateDigest(digestType.uri());
}
byte[] calculateDigestInternal(DigestAlgorithm digestAlgorithm) {
logger.debug("Digest algorithm: " + digestAlgorithm);
return DSSUtils.digest(digestAlgorithm, getBytes());
}
/**
* Returns the data file name.
*
* @return filename
*/
public String getName() {
String documentName = document.getName();
String name = new File(documentName).getName();
logger.trace("File name: for document " + documentName + " is " + name);
return name;
}
/**
* Returns file ID
* For BDoc it will return the filename
*
* @return id or name
*/
public String getId() {
logger.debug("");
return (id == null ? getName() : id);
}
/**
* Returns the data file size.
*
* @return file size in bytes
*/
public long getFileSize() {
logger.debug("");
long fileSize;
if (document instanceof StreamDocument || document instanceof FileDocument) {
try {
fileSize = Files.size(Paths.get(document.getAbsolutePath()));
logger.debug("Document size: " + fileSize);
return fileSize;
} catch (IOException e) {
logger.error(e.getMessage());
throw new DigiDoc4JException(e);
}
}
fileSize = getBytes().length;
logger.debug("File document size: " + fileSize);
return fileSize;
}
/**
* Returns the file media type.
*
* @return media type
*/
public String getMediaType() {
logger.debug("");
String mediaType = document.getMimeType().getMimeTypeString();
logger.debug("Media type is: " + mediaType);
return mediaType;
}
public void setMediaType(String mediaType) {
MimeType mimeType = getMimeType(mediaType);
document.setMimeType(mimeType);
}
/**
* Saves a copy of the data file as a file to the specified stream.
*
* @param out stream where data is written to
* @throws java.io.IOException on file write error
*/
public void saveAs(OutputStream out) throws IOException {
logger.debug("");
out.write(getBytes());
}
/**
* Saves a copy of the data file as a file with the specified file name.
*
* @param path full file path where the data file should be saved to. If the file exists it will be overwritten
*/
//TODO exception - method throws DSSException which can be caused by other exceptions
public void saveAs(String path) {
try {
logger.debug("Path: " + path);
document.save(path);
} catch (IOException e) {
logger.error("Failed to save path " + path);
throw new TechnicalException("Failed to save path " + path, e);
}
}
/**
* Gives file bytes
*
* @return data as bytes
*/
public byte[] getBytes() {
logger.debug("");
try {
return IOUtils.toByteArray(document.openStream());
} catch (IOException e) {
throw new TechnicalException("Error reading document bytes: " + e.getMessage(), e);
}
}
/**
* Gives data file as stream
*
* @return data file stream
*/
public InputStream getStream() {
logger.debug("");
return document.openStream();
}
/**
* Set id for the dataFile (DDoc usage only)
*
* @param dataFileId id for the dataFile
*/
public void setId(String dataFileId) {
logger.debug("");
this.id = dataFileId;
}
public DSSDocument getDocument() {
return document;
}
protected void setDocument(DSSDocument document) {
this.document = document;
}
}