/* 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.impl.bdoc.asic;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.digidoc4j.DataFile;
import org.digidoc4j.Signature;
import org.digidoc4j.exceptions.TechnicalException;
import org.digidoc4j.impl.bdoc.manifest.AsicManifest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.MimeType;
public class AsicContainerCreator {
private static final Logger logger = LoggerFactory.getLogger(AsicContainerCreator.class);
private final static String ZIP_ENTRY_MIMETYPE = "mimetype";
private static final Charset CHARSET = StandardCharsets.UTF_8;
private ZipOutputStream zipOutputStream;
private ByteArrayOutputStream outputStream;
private String zipComment;
public AsicContainerCreator(File containerPathToSave) {
logger.debug("Starting to save bdoc zip container to " + containerPathToSave);
try {
FileOutputStream outputStream = new FileOutputStream(containerPathToSave);
zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream), CHARSET);
} catch (FileNotFoundException e) {
logger.error("Unable to create BDoc ZIP container: " + e.getMessage());
throw new TechnicalException("Unable to create BDoc ZIP container", e);
}
}
public AsicContainerCreator() {
outputStream = new ByteArrayOutputStream();
zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream), CHARSET);
}
public void finalizeZipFile() {
logger.debug("Finalizing bdoc zip file");
try {
zipOutputStream.close();
} catch (IOException e) {
logger.error("Unable to finish creating BDoc ZIP container: " + e.getMessage());
throw new TechnicalException("Unable to finish creating BDoc ZIP container", e);
}
}
public InputStream fetchInputStreamOfFinalizedContainer() {
logger.debug("Fetching input stream of the finalized container");
return new ByteArrayInputStream(outputStream.toByteArray());
}
public void writeAsiceMimeType() {
logger.debug("Writing asic mime type to bdoc zip file");
String mimeTypeString = MimeType.ASICE.getMimeTypeString();
byte[] mimeTypeBytes = mimeTypeString.getBytes();
ZipEntry entryMimetype = getAsicMimeTypeZipEntry(mimeTypeBytes);
writeZipEntry(entryMimetype, mimeTypeBytes);
}
public void writeManifest(Collection<DataFile> dataFiles) {
logger.debug("Writing bdoc manifest");
AsicManifest manifest = new AsicManifest();
manifest.addFileEntry(dataFiles);
byte[] entryBytes = manifest.getBytes();
writeZipEntry(new ZipEntry(AsicManifest.XML_PATH), entryBytes);
}
public void writeDataFiles(Collection<DataFile> dataFiles) {
logger.debug("Adding data files to the bdoc zip container");
for (DataFile dataFile : dataFiles) {
String name = dataFile.getName();
logger.debug("Adding data file " + name);
ZipEntry entryDocument = new ZipEntry(name);
zipOutputStream.setLevel(ZipEntry.DEFLATED);
byte[] entryBytes = dataFile.getBytes();
writeZipEntry(entryDocument, entryBytes);
}
}
public void writeSignatures(Collection<Signature> signatures, int nextSignatureFileNameIndex) {
logger.debug("Adding signatures to the bdoc zip container");
int index = nextSignatureFileNameIndex;
for (Signature signature : signatures) {
String signatureFileName = "META-INF/signatures" + index + ".xml";
ZipEntry entryDocument = new ZipEntry(signatureFileName);
byte[] entryBytes = signature.getAdESSignature();
writeZipEntry(entryDocument, entryBytes);
index++;
}
}
public void writeExistingEntries(Collection<AsicEntry> asicEntries) {
logger.debug("Writing existing zip container entries");
for (AsicEntry asicEntry : asicEntries) {
DSSDocument content = asicEntry.getContent();
byte[] entryBytes = getDocumentBytes(content);
ZipEntry zipEntry = asicEntry.getZipEntry();
if(!StringUtils.equalsIgnoreCase(ZIP_ENTRY_MIMETYPE, zipEntry.getName())) {
zipOutputStream.setLevel(ZipEntry.DEFLATED);
}
writeZipEntryWithoutComment(zipEntry, entryBytes);
}
}
public void writeContainerComment(String comment) {
logger.debug("Writing container comment: " + comment);
zipOutputStream.setComment(comment);
}
public void setZipComment(String zipComment) {
this.zipComment = zipComment;
}
private ZipEntry getAsicMimeTypeZipEntry(byte[] mimeTypeBytes) {
ZipEntry entryMimetype = new ZipEntry(ZIP_ENTRY_MIMETYPE);
entryMimetype.setMethod(ZipEntry.STORED);
entryMimetype.setSize(mimeTypeBytes.length);
entryMimetype.setCompressedSize(mimeTypeBytes.length);
CRC32 crc = new CRC32();
crc.update(mimeTypeBytes);
entryMimetype.setCrc(crc.getValue());
return entryMimetype;
}
private void writeZipEntry(ZipEntry zipEntry, byte[] entryBytes) {
zipEntry.setComment(zipComment);
writeZipEntryWithoutComment(zipEntry, entryBytes);
}
private void writeZipEntryWithoutComment(ZipEntry zipEntry, byte[] entryBytes) {
try {
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.write(entryBytes);
zipOutputStream.closeEntry();
} catch (IOException e) {
logger.error("Unable to write Zip entry to BDoc container: " + e.getMessage());
throw new TechnicalException("Unable to write Zip entry to BDoc container", e);
}
}
private byte[] getDocumentBytes(DSSDocument content) {
try {
return IOUtils.toByteArray(content.openStream());
} catch (IOException e) {
logger.error("Error getting document content: " + e.getMessage());
throw new TechnicalException("Error getting document content: " + e.getMessage(), e);
}
}
}