package railo.commons.io.res.type.ftp; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPFile; import railo.commons.date.JREDateTimeUtil; import railo.commons.io.IOUtil; import railo.commons.io.ModeUtil; import railo.commons.io.res.Resource; import railo.commons.io.res.ResourceProvider; import railo.commons.io.res.util.ResourceSupport; import railo.commons.io.res.util.ResourceUtil; import railo.commons.lang.StringUtil; import railo.runtime.PageContext; import railo.runtime.engine.ThreadLocalPageContext; import railo.runtime.op.Caster; public final class FTPResource extends ResourceSupport { private final FTPResourceProvider provider; private final String path; private final String name; private final FTPConnectionData data; /** * Constructor of the class * @param factory * @param data * @param path */ FTPResource(FTPResourceProvider provider, FTPConnectionData data, String path) { this.provider=provider; this.data=data; String[] pathName=ResourceUtil.translatePathName(path); this.path=pathName[0]; this.name=pathName[1]; } /** * Constructor of the class * @param factory * @param data * @param path */ private FTPResource(FTPResourceProvider provider, FTPConnectionData data, String path,String name) { this.provider=provider; this.data=data; this.path=path; this.name=name; } @Override public boolean isReadable() { Boolean rtn = hasPermission(FTPFile.READ_PERMISSION); if(rtn==null) return false; return rtn.booleanValue(); } public boolean isWriteable() { Boolean rtn = hasPermission(FTPFile.WRITE_PERMISSION); if(rtn==null) return false; return rtn.booleanValue(); } private Boolean hasPermission(int permission) { FTPResourceClient client=null; try { provider.read(this); client=provider.getClient(data); FTPFile file=client.getFTPFile(this); if(file==null) return null; return Caster.toBoolean(file.hasPermission(FTPFile.USER_ACCESS,permission) || file.hasPermission(FTPFile.GROUP_ACCESS,permission) || file.hasPermission(FTPFile.WORLD_ACCESS,permission)); } catch (IOException e) { return Boolean.FALSE; } finally { provider.returnClient(client); } } @Override public void remove(boolean alsoRemoveChildren) throws IOException { if(isRoot()) throw new FTPResoucreException("can't delete root of ftp server"); if(alsoRemoveChildren)ResourceUtil.removeChildren(this); FTPResourceClient client=null; try { provider.lock(this); client = provider.getClient(data); boolean result = client.deleteFile(getInnerPath()); if(!result) throw new IOException("can't delete file ["+getPath()+"]"); } finally { provider.returnClient(client); provider.unlock(this); } } @Override public boolean delete() { if(isRoot()) return false; FTPResourceClient client = null; try { provider.lock(this); client = provider.getClient(data); return client.deleteFile(getInnerPath()); } catch (IOException e) { return false; } finally { provider.returnClient(client); provider.unlock(this); } } @Override public boolean exists() { try { provider.read(this); } catch (IOException e) { return true; } FTPResourceClient client = null; InputStream is=null; try { // getClient muss zuerst sein so wird verbindung gepr゚ft client = provider.getClient(data); if(isRoot()) return true; FTPFile file = client.getFTPFile(this); if(file!=null) { return !file.isUnknown(); } //String pathname = getInnerPath(); String p = getInnerPath(); if(!StringUtil.endsWith(p, '/'))p+="/"; if(client.listNames(p)!=null) return true; return false; } catch (IOException e) { return false; } finally { IOUtil.closeEL(is); provider.returnClient(client); } } @Override public String getName() { return name; } @Override public String getParent() { if(isRoot()) return null; return provider.getScheme().concat("://").concat(data.key()).concat(path.substring(0,path.length()-1)); } public String getInnerParent() { return path; } @Override public Resource getParentResource() { if(isRoot()) return null; return new FTPResource(provider,data,path); } @Override public Resource getRealResource(String realpath) { realpath=ResourceUtil.merge(getInnerPath(), realpath); if(realpath.startsWith("../"))return null; return new FTPResource(provider,data,realpath); } @Override public String getPath() { return provider.getScheme().concat("://").concat(data.key()).concat(path).concat(name); } /** * @return returns path starting from ftp root */ String getInnerPath() { return path.concat(name); } @Override public boolean isAbsolute() { // TODO impl isAbolute return true; } @Override public boolean isDirectory() { try { provider.read(this); } catch (IOException e1) { return false; } FTPResourceClient client=null; try { // getClient muss zuerst sein so wird verbindung gepr゚ft client = provider.getClient(data); if(isRoot())return true; FTPFile file = client.getFTPFile(this); if(file!=null) { return file.isDirectory(); } //if(file==null) return false; //return file.isDirectory(); String p = getInnerPath(); if(!StringUtil.endsWith(p, '/'))p+="/"; return client.listNames(p)!=null; } catch (IOException e) { return false; } finally { provider.returnClient(client); } } @Override public boolean isFile() { if(isRoot()) return false; try { provider.read(this); } catch (IOException e1) { return false; } FTPResourceClient client=null; InputStream is=null; try { client = provider.getClient(data); FTPFile file = client.getFTPFile(this); if(file!=null) { return file.isFile(); } return false; //String pathname = getInnerPath(); //return (is=client.retrieveFileStream(pathname))!=null; } catch (IOException e) { return false; } finally { IOUtil.closeEL(is); provider.returnClient(client); } } @Override public long lastModified() { //if(isRoot()) return 0; FTPResourceClient client=null; try { provider.read(this); client=provider.getClient(data); FTPFile file = client.getFTPFile(this); if(file==null) return 0; return file.getTimestamp().getTimeInMillis(); } catch (IOException e) { return 0; } finally { provider.returnClient(client); } } @Override public long length() { if(isRoot()) return 0; FTPResourceClient client=null; try { provider.read(this); client = provider.getClient(data); FTPFile file = client.getFTPFile(this); if(file==null) return 0; return file.getSize(); } catch (IOException e) { return 0; } finally { provider.returnClient(client); } } @Override public Resource[] listResources() { if(isFile()) return null;//new Resource[0]; FTPResourceClient client=null; try { client = provider.getClient(data); FTPFile[] files=null; String p = getInnerPath(); if(!StringUtil.endsWith(p, '/'))p+="/"; files=client.listFiles(p); if(files==null) return new Resource[0]; List list=new ArrayList(); String parent=path.concat(name).concat("/"); String name; FTPResource res; for(int i=0;i<files.length;i++) { name=files[i].getName(); if(!".".equals(name) && !"..".equals(name)) { res=new FTPResource(provider,data,parent,name); client.registerFTPFile(res, files[i]); list.add(res); } } return (Resource[]) list.toArray(new FTPResource[list.size()]); } catch(IOException ioe) { return null; } finally { provider.returnClient(client); } } @Override public boolean setLastModified(long time) { //if(isRoot()) return false; FTPResourceClient client=null; try { provider.lock(this); client=provider.getClient(data); PageContext pc = ThreadLocalPageContext.get(); Calendar c=JREDateTimeUtil.getThreadCalendar(); if(pc!=null)c.setTimeZone(pc.getTimeZone()); c.setTimeInMillis(time); FTPFile file = client.getFTPFile(this); if(file==null) return false; file.setTimestamp(c); client.unregisterFTPFile(this); return true; } catch (IOException e) {} finally { provider.returnClient(client); provider.unlock(this); } return false; } public boolean setReadOnly() { try { setMode(ModeUtil.setWritable(getMode(), false)); return true; } catch (IOException e) { return false; } } @Override public void createFile(boolean createParentWhenNotExists) throws IOException { ResourceUtil.checkCreateFileOK(this, createParentWhenNotExists); //client.unregisterFTPFile(this); IOUtil.copy(new ByteArrayInputStream(new byte[0]), getOutputStream(), true, true); } @Override public void moveTo(Resource dest) throws IOException { FTPResourceClient client=null; ResourceUtil.checkMoveToOK(this, dest); try { provider.lock(this); client = provider.getClient(data); client.unregisterFTPFile(this); if(dest instanceof FTPResource) moveTo(client,(FTPResource)dest); else super.moveTo(dest); } finally { provider.returnClient(client); provider.unlock(this); } } private void moveTo(FTPResourceClient client, FTPResource dest) throws IOException { if(!dest.data.equals(data)) { super.moveTo(dest); return; } if(dest.exists())dest.delete(); client.unregisterFTPFile(dest); boolean ok = client.rename(getInnerPath(), dest.getInnerPath()); if(!ok) throw new IOException("can't create file "+this); } @Override public void createDirectory(boolean createParentWhenNotExists) throws IOException { ResourceUtil.checkCreateDirectoryOK(this, createParentWhenNotExists); FTPResourceClient client=null; try { provider.lock(this); client = provider.getClient(data); client.unregisterFTPFile(this); boolean ok = client.makeDirectory(getInnerPath()); if(!ok) throw new IOException("can't create file "+this); } finally { provider.returnClient(client); provider.unlock(this); } } @Override public InputStream getInputStream() throws IOException { ResourceUtil.checkGetInputStreamOK(this); provider.lock(this); FTPResourceClient client=provider.getClient(data); client.setFileType(FTP.BINARY_FILE_TYPE); try { return IOUtil.toBufferedInputStream(new FTPResourceInputStream(client,this,client.retrieveFileStream(getInnerPath()))); } catch (IOException e) { provider.returnClient(client); provider.unlock(this); throw e; } } @Override public OutputStream getOutputStream(boolean append) throws IOException { ResourceUtil.checkGetOutputStreamOK(this); FTPResourceClient client=null; try { provider.lock(this); client=provider.getClient(data); client.unregisterFTPFile(this); client.setFileType(FTP.BINARY_FILE_TYPE); OutputStream os = append?client.appendFileStream(getInnerPath()):client.storeFileStream(getInnerPath()); if(os==null)throw new IOException("can not open stream to file ["+this+"]"); return IOUtil.toBufferedOutputStream(new FTPResourceOutputStream(client,this,os)); } catch (IOException e) { provider.returnClient(client); provider.unlock(this); throw e; } } @Override public String[] list() { if(isFile()) return new String[0]; FTPResourceClient client=null; try { client = provider.getClient(data); String[] files=null; String p = getInnerPath(); if(!StringUtil.endsWith(p, '/'))p+="/"; files=client.listNames(p); if(files==null) return new String[0]; for(int i=0;i<files.length;i++) { files[i]=cutName(files[i]); } return files; } catch(IOException ioe) { return null; } finally { provider.returnClient(client); } } private String cutName(String path) { int index=path.lastIndexOf('/'); if(index==-1) return path; return path.substring(index+1); } @Override public ResourceProvider getResourceProvider() { return provider; } public FTPResourceProvider getFTPResourceProvider() { return provider; } boolean isRoot() { return StringUtil.isEmpty(name); } public int getMode() { //if(isRoot()) return 0; FTPResourceClient client=null; try { provider.read(this); client=provider.getClient(data); FTPFile file = client.getFTPFile(this); int mode=0; if(file==null)return 0; // World if(file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION)) mode+=01; if(file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION)) mode+=02; if(file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION)) mode+=04; // Group if(file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION)) mode+=010; if(file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION)) mode+=020; if(file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION)) mode+=040; // Owner if(file.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION)) mode+=0100; if(file.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)) mode+=0200; if(file.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)) mode+=0400; return mode; } catch (IOException e) {} finally { provider.returnClient(client); } return 0; } public void setMode(int mode) throws IOException { //if(isRoot()) throw new IOException("can't change mode of root"); FTPResourceClient client=null; try { provider.lock(this); client=provider.getClient(data); FTPFile file = client.getFTPFile(this); if(file!=null) { // World file.setPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION,(mode&01)>0); file.setPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION,(mode&02)>0); file.setPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION,(mode&04)>0); // Group file.setPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION,(mode&010)>0); file.setPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION,(mode&020)>0); file.setPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION,(mode&040)>0); // Owner file.setPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION,(mode&0100)>0); file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION,(mode&0200)>0); file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION,(mode&0400)>0); client.unregisterFTPFile(this); } } catch (IOException e) {} finally { provider.returnClient(client); provider.unlock(this); } } public boolean setReadable(boolean value) { try { setMode(ModeUtil.setReadable(getMode(), value)); return true; } catch (IOException e) { return false; } } public boolean setWritable(boolean value) { try { setMode(ModeUtil.setWritable(getMode(), value)); return true; } catch (IOException e) { return false; } } }