/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.ctp.stream;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
import java.nio.file.Paths;
import org.bouncycastle.openpgp.PGPException;
import divconq.ctp.f.FileDescriptor;
import divconq.lang.op.OperationContext;
import divconq.log.Logger;
import divconq.pgp.EncryptedFileStream;
import divconq.script.StackEntry;
import divconq.util.FileUtil;
import divconq.xml.XElement;
public class PgpEncryptStream extends BaseStream implements IStreamSource {
protected EncryptedFileStream pgp = new EncryptedFileStream();
protected boolean needInit = true;
protected FileDescriptor efile = null;
//protected String ourpath = null;
//protected long ourmod = 0;
//protected byte ourperm = 0;
public PgpEncryptStream() {
}
@Override
public void init(StackEntry stack, XElement el) {
String keyPath = stack.stringFromElement(el, "Keyring");
try {
this.pgp.loadPublicKey(Paths.get(keyPath));
}
catch (IOException x) {
OperationContext.get().error("Unabled to read keyfile: " + x);
}
catch (PGPException x) {
OperationContext.get().error("Unabled to load keyfile: " + x);
}
}
@Override
public void close() {
try {
this.pgp.close();
}
catch (PGPException x) {
// it should already be closed, unless we got here by a task kill/cancel
Logger.warn("Error closing PGP stream: " + x);
}
super.close();
}
// make sure we don't return without first releasing the file reference content
@Override
public ReturnOption handle(FileDescriptor file, ByteBuf data) {
if (file == FileDescriptor.FINAL)
return this.downstream.handle(file, data);
if (this.needInit) {
this.pgp.setFileName(file.path().getFileName());
try {
this.pgp.init();
}
catch (Exception x) {
OperationContext.get().getTaskRun().kill("PGP init failed: " + x);
return ReturnOption.DONE;
}
this.initializeFileValues(file);
this.needInit = false;
}
// inflate the payload into 1 or more outgoing buffers set in a queue
ByteBuf in = data;
if (in != null) {
this.pgp.writeData(in);
in.release();
if (OperationContext.get().getTaskRun().isKilled())
return ReturnOption.DONE;
}
// write all buffers in the queue
ByteBuf buf = this.pgp.nextReadyBuffer();
while (buf != null) {
ReturnOption ret = this.nextMessage(buf);
if (ret != ReturnOption.CONTINUE)
return ret;
buf = this.pgp.nextReadyBuffer();
}
if (file.isEof()) {
try {
this.pgp.close();
}
catch (PGPException x) {
OperationContext.get().getTaskRun().kill("PGP close failed: " + x);
return ReturnOption.DONE;
}
// write all buffers in the queue
buf = this.pgp.nextReadyBuffer();
while (buf != null) {
ReturnOption ret = this.nextMessage(buf);
if (ret != ReturnOption.CONTINUE)
return ret;
buf = this.pgp.nextReadyBuffer();
}
ReturnOption ret = this.lastMessage();
if (ret != ReturnOption.CONTINUE)
return ret;
}
// otherwise we need more data
return ReturnOption.CONTINUE;
}
public ReturnOption nextMessage(ByteBuf out) {
return this.downstream.handle(this.efile, out);
}
public ReturnOption lastMessage() {
this.efile.setEof(true);
return this.downstream.handle(this.efile, null);
}
public void initializeFileValues(FileDescriptor src) {
this.efile = new FileDescriptor();
if (src.hasPath())
this.efile.setPath(src.getPath().toString() + ".gpg");
else
this.efile.setPath("/" + FileUtil.randomFilename("bin") + ".gpg");
this.efile.setModTime(src.getModTime());
this.efile.setPermissions(src.getPermissions());
}
@Override
public void read() {
// write all buffers in the queue
ByteBuf buf = this.pgp.nextReadyBuffer();
while (buf != null) {
ReturnOption ret = this.nextMessage(buf);
if (ret != ReturnOption.CONTINUE)
return;
buf = this.pgp.nextReadyBuffer();
}
// if we reached done and we wrote all the buffers, then send the EOF marker if not already
if (this.pgp.isClosed()) {
ReturnOption ret = this.lastMessage();
if (ret != ReturnOption.CONTINUE)
return;
}
this.upstream.read();
}
}