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;
}
}
}