It looks like the mailing list strips attachments. Here's the source, pasted in:
Unfortunately there are some imports that I can't provide you, but you should be able to knock up a substitute. ------------------------------------- package com.example.apachesftp.filesystem.delegating; import java.io.IOException; import java.util.*; import java.util.Map.Entry; import org.apache.sshd.common.Session; import org.apache.sshd.server.FileSystemFactory; import org.apache.sshd.server.FileSystemView; public class DelegatingFilesystemFactory implements FileSystemFactory { Map<String, FileSystemFactory> delegateFactories = new TreeMap<String, FileSystemFactory>(); @Override public FileSystemView createFileSystemView(final Session session) throws IOException { Map<String, FileSystemView> delegates = new TreeMap<String, FileSystemView>(); for (Entry<String, FileSystemFactory> entry : delegateFactories .entrySet()) { delegates.put(entry.getKey(), entry.getValue().createFileSystemView(session)); } return new DelegatingFilesystemView(session, delegates); } /** * Provides a set of FileSystemFactories from which delegate FileSystemViews * are created. * * @param delegateFactories * A map. The keys are the names of the "directories" used for * these delegates; the values are the FileSystemFactories. */ public void setDelegateFactories( final Map<String, FileSystemFactory> delegateFactories) { this.delegateFactories = delegateFactories; } } --------------------------------------- package com.example.apachesftp.filesystem.delegating; import java.io.*; import java.util.*; import org.apache.sshd.server.FileSystemView; import org.apache.sshd.server.SshFile; import com.example.apachesftp.filesystem.*; public class DelegatingSshFile implements DelegatableSshDirectory { public enum Disposition { ROOT_OF_EVERYTHING, ROOT_OF_DELEGATE, IN_DELEGATE } private final String root; private final SshFile delegate; private final Map<String, FileSystemView> delegates; private String alias = null; private DelegatableSshDirectory parent = this; /* * Some notes. * * If absolute path is empty or "/" then we are the root of the delegating * filesystem. * * If absolute path is exactly one element long, then we are at the root of a * delegate * * If the absolute path is 2 elements or more, then we are deeper into a * delegate. */ public DelegatingSshFile(String path, final Map<String, FileSystemView> delegates) { if (path.equals(".")) { path = "/"; } String[] parts = PathUtil.headAndTail(path); this.root = parts[0]; this.delegates = delegates; FileSystemView delegateFSV = delegates.get(root); if (null != delegateFSV) { this.delegate = delegateFSV.getFile(parts[1]); } else { this.delegate = new NonExistantSshFile(parts[1]); } } public DelegatingSshFile(final String root, final SshFile delegate, final Map<String, FileSystemView> delegates) { this.root = root; this.delegate = delegate; this.delegates = delegates; } public Disposition getDisposition() { if (PathUtil.isBottom(root)) { return Disposition.ROOT_OF_EVERYTHING; } if (delegate instanceof NonExistantSshFile) { if (PathUtil.isBottom(delegate.getAbsolutePath())) { return Disposition.ROOT_OF_EVERYTHING; } else { return Disposition.IN_DELEGATE; } } if (PathUtil.isBottom(delegate.getAbsolutePath())) { return Disposition.ROOT_OF_DELEGATE; } // else return Disposition.IN_DELEGATE; } @Override public String getAbsolutePath() { if (PathUtil.isBottom(root)) { return "/"; } else { return PathUtil.joinPaths(root, delegate.getAbsolutePath()); } } @Override public String getName() { if(alias != null) { return alias; } else { return getOriginalName(); } } private String getOriginalName() { String name = delegate.getName(); if (PathUtil.isBottom(name)) { name = root; } name = PathUtil.deSlashFront(name); if (name.equals("")) { name = "/"; } return name; } @Override public boolean isDirectory() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return true; default: return delegate.isDirectory(); } } @Override public boolean isFile() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return false; default: return delegate.isFile(); } } @Override public boolean doesExist() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: if(root.equals("")) { return true; } else { return delegates.keySet().contains(root); } default: return delegate.doesExist(); } } @Override public boolean isReadable() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return true; default: return delegate.isReadable(); } } @Override public boolean isWritable() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return false; default: return delegate.isWritable(); } } @Override public boolean isExecutable() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return true; default: return delegate.isExecutable(); } } @Override public boolean isRemovable() { return delegate.isRemovable(); } @Override public SshFile getParentFile() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return this; case ROOT_OF_DELEGATE: return new DelegatingSshFile("/", delegates); default: return new DelegatingSshFile(root, delegate.getParentFile(), delegates); } } @Override public long getLastModified() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return 0; default: return delegate.getLastModified(); } } @Override public boolean setLastModified(final long time) { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return false; default: return delegate.setLastModified(time); } } @Override public long getSize() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return delegates.size(); // plus . and .. default: return delegate.getSize(); } } @Override public boolean mkdir() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return false; default: return delegate.mkdir(); } } @Override public boolean delete() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return false; default: return delegate.delete(); } } @Override public boolean create() throws IOException { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return false; default: return delegate.create(); } } @Override public void truncate() throws IOException { } @Override public boolean move(final SshFile destination) { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return false; default: return delegate.move(destination); } } @Override public List<SshFile> listSshFiles() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return listDelegates(); default: List<SshFile> files = delegate.listSshFiles(); return delegatify(files); } } private List<SshFile> listDelegates() { List<SshFile> l = new LinkedList<SshFile>(); l.add(copyWithAlias(".")); l.add(parent.copyWithAlias("..")); Set<String> keys = this.delegates.keySet(); for (String key : keys) { l.add(new DelegatingSshFile(key, delegates)); } return l; } private List<SshFile> delegatify(final List<SshFile> plainFiles) { List<SshFile> l = new LinkedList<SshFile>(); if(null != plainFiles) { for (SshFile f : plainFiles) { l.add(new DelegatingSshFile(root, f, delegates)); } } return l; } @Override public OutputStream createOutputStream(final long offset) throws IOException { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return null; default: return delegate.createOutputStream(offset); } } @Override public InputStream createInputStream(final long offset) throws IOException { return delegate.createInputStream(offset); } @Override public void handleClose() throws IOException { switch (getDisposition()) { case ROOT_OF_EVERYTHING: break; default: delegate.handleClose(); } } @Override public void setAlias(String alias) { this.alias = alias; } @Override public DelegatingSshFile copyWithAlias(String alias) { DelegatingSshFile copy = new DelegatingSshFile(root,delegate,delegates); copy.setAlias(alias); return copy; } @Override public void setParent(DelegatableSshDirectory parent) { this.parent = parent; } @Override public String getOwner() { switch (getDisposition()) { case ROOT_OF_EVERYTHING: return "system"; default: return delegate.getOwner(); } } public SshFile getDelegate() { return delegate; } } ------------------------ package com.example.apachesftp.filesystem.delegating; import java.util.Map; import org.apache.sshd.common.Session; import org.apache.sshd.server.FileSystemView; import org.apache.sshd.server.SshFile; public class DelegatingFilesystemView implements FileSystemView { private Map<String, FileSystemView> delegates; public DelegatingFilesystemView(final Session session, final Map<String, FileSystemView> delegates) { setDelegates(delegates); } public void setDelegates(final Map<String, FileSystemView> delegates) { this.delegates = delegates; } @Override public SshFile getFile(final String path) { DelegatingSshFile f = new DelegatingSshFile( PathUtil.cleanDots(path), delegates); if(f instanceof DelegatableSshDirectory) { DelegatingSshFile root = new DelegatingSshFile("/", delegates); f.setParent(root); } return f; } @Override public SshFile getFile(final SshFile baseDir, final String file) { String fullPath = PathUtil.joinPaths(baseDir.getAbsolutePath(), file); return getFile(fullPath); } } On 16 October 2013 15:20, Gentian Hila <gh...@commercehub.com> wrote: > Thank you very much, > > I will take a look around the interfaces you suggest. I did not get any > attached files though. > > > Genti > > From: John Hartnup [mailto:john.hart...@gmail.com] > Sent: Wednesday, October 16, 2013 9:58 AM > To: ftpserver-users@mina.apache.org > Subject: Re: directory structure permissions & account locking > > The answer to any question like this is to customise the Filesystem > classes to do what you want. > > I've attached some Java files (not warrantied for any particular purpose) > for "DelegatingFilesystem" -- you can use one of these as the top of your > filesystem hierarchy, with real, separately configured, filesystems as > delegates, which appear as subdirectories. > > To grok these, you'll need to grok the FilesystemView, > FilesystemViewFactory and SshFile interfaces - read the interfaces and > they'll probably make sense. > > > > On 16 October 2013 14:50, Gentian Hila <gh...@commercehub.com<mailto: > gh...@commercehub.com>> wrote: > We have a use case where we would want to have two subdirectories under > the user home directory incoming and outgoing. Each of them would have a > hierarchical path. > > However, we want: > > User have read-only permissions for the outgoing directory and its > children. > User have read-write permissions for the incoming directory and its > children. > Users cannot change the structure of the directories ( I think we can > achieve that by disabling MKDIR and RMD). > Users cannot delete a file ( we can achieve that by disabling DELE > command). > > However I do not see a way how to apply different permissions to different > folders either through configuration or through extending the API source > code yet. > > Is this possible at all? Has anybody attempted to do this? > > > > > We also have a use case that basically requires that we lock an account > after several failed logins. I did not see a mechanism for account locking > and unlocking when this happens? > > Am I missing something? > > > Genti > > > > -- > "There is no way to peace; peace is the way" > -- "There is no way to peace; peace is the way"