/* ************************************************************************ # # 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.interchange.sshd; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.channels.Channels; import java.nio.channels.Pipe; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.sshd.common.file.SshFile; import divconq.api.ApiSession; import divconq.bus.Message; import divconq.filestore.CommonPath; import divconq.hub.Hub; import divconq.lang.op.FuncCallback; import divconq.lang.op.OperationCallback; import divconq.log.Logger; import divconq.struct.RecordStruct; import divconq.work.IWork; import divconq.work.TaskRun; public class SshFileImpl implements SshFile { protected CommonPath filePath = null; protected FileSystemViewImpl fsView = null; protected SshFileImpl(FileSystemViewImpl fsview, CommonPath filePath) { if (fsview == null) throw new IllegalArgumentException("fsview can not be null"); if (filePath == null) throw new IllegalArgumentException("fileName can not be null"); this.fsView = fsview; this.filePath = filePath; } @Override public String getAbsolutePath() { return this.filePath.toString(); } @Override public String getName() { return this.filePath.getFileName(); } @Override public String getOwner() { return null; } @Override public boolean isDirectory() { return false; } @Override public boolean isFile() { return true; } @Override public boolean doesExist() { return false; } @Override public long getSize() { System.out.println("get size called"); return 0; } @Override public long getLastModified() { return 0; } @Override public boolean setLastModified(long time) { return false; } @Override public boolean isReadable() { return false; } @Override public boolean isWritable() { return true; } @Override public boolean isRemovable() { return false; } @Override public SshFile getParentFile() { return this.fsView.getRoot(); } @Override public boolean delete() { return false; } @Override public boolean create() { System.out.println("Create called"); return true; } // TODO support offset and such @Override public OutputStream createOutputStream(final long offset) throws IOException { System.out.println("Create output stream called"); final ApiSession api = this.fsView.getApi(); final Pipe p = Pipe.open(); // TODO final String token = "abc9"; RecordStruct rec = new RecordStruct(); rec.setField("FileName", this.filePath.getFileName()); rec.setField("FileSize", 0); rec.setField("AuthToken", token); Message msg = new Message("nccUploader", "Deposits", "StartUpload", rec); // TODO make sure this results in proper user context in local session // calling this puts the result into another thread api.establishDataStream("Uploading " + this.filePath, "Upload", msg, new FuncCallback<RecordStruct>() { @Override public void callback() { if (this.hasErrors()) { Logger.error("Start Upload error: " + this.getMessage()); try { // so SFTP knows not to continue p.sink().close(); } catch (IOException x) { System.out.println("Error closing sshd output stream");; } return; } System.out.println("Upload request good, sending stream - need new thread [for keep alive]."); final RecordStruct sinfo = this.getResult(); // TODO this means we are limited in number of uploads by size of WorkPool // TODO figure out how not to overload the pool Hub.instance.getWorkPool().submit(new IWork() { @Override public void run(final TaskRun run) { api.sendStream(p.source(), 0, offset, sinfo.getFieldAsString("ChannelId"), new OperationCallback() { @Override public void callback() { if (this.hasErrors()) System.out.println("Upload failed!"); else System.out.println("Upload success!"); run.complete(); } }); } }); } }); return Channels.newOutputStream(p.sink()); } @Override public void truncate() { System.out.println("Truncate called"); } @Override public boolean move(final SshFile dest) { return false; } @Override public boolean mkdir() { return false; } @Override public List<SshFile> listSshFiles() { return null; } @Override public InputStream createInputStream(long offset) { return null; } @Override public void handleClose() { System.out.println("handle close called"); // Noop } @Override public boolean equals(Object obj) { if (obj != null && obj instanceof SshFileImpl) return this.filePath.equals(((SshFileImpl)obj).filePath); return false; } @Override public String toString() { return this.filePath.toString(); } @Override public Map<Attribute, Object> getAttributes(boolean followLinks) throws IOException { Map<Attribute, Object> map = new HashMap<Attribute, Object>(); map.put(Attribute.Size, getSize()); map.put(Attribute.IsDirectory, isDirectory()); map.put(Attribute.IsRegularFile, isFile()); map.put(Attribute.IsSymbolicLink, false); map.put(Attribute.LastModifiedTime, getLastModified()); map.put(Attribute.LastAccessTime, getLastModified()); map.put(Attribute.Owner, "ncc"); map.put(Attribute.Group, "ncc"); EnumSet<Permission> p = EnumSet.noneOf(Permission.class); if (isReadable()) { p.add(Permission.UserRead); p.add(Permission.GroupRead); p.add(Permission.OthersRead); } if (isWritable()) { p.add(Permission.UserWrite); p.add(Permission.GroupWrite); p.add(Permission.OthersWrite); } if (isExecutable()) { p.add(Permission.UserExecute); p.add(Permission.GroupExecute); p.add(Permission.OthersExecute); } map.put(Attribute.Permissions, p); return map; } @Override public void setAttributes(Map<Attribute, Object> attributes) throws IOException { System.out.println("set attributes called"); if (!attributes.isEmpty()) { throw new UnsupportedOperationException(); } } @Override public Object getAttribute(Attribute attribute, boolean followLinks) throws IOException { return getAttributes(followLinks).get(attribute); } @Override public void setAttribute(Attribute attribute, Object value) throws IOException { System.out.println("set attribute called"); Map<Attribute, Object> map = new HashMap<Attribute, Object>(); map.put(attribute, value); setAttributes(map); } @Override public String readSymbolicLink() throws IOException { throw new UnsupportedOperationException(); } @Override public void createSymbolicLink(SshFile destination) throws IOException { throw new UnsupportedOperationException(); } @Override public boolean isExecutable() { return false; } }