package railo.runtime.tag;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import railo.commons.io.IOUtil;
import railo.commons.io.res.Resource;
import railo.commons.io.res.util.ResourceUtil;
import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.PageException;
import railo.runtime.ext.tag.TagImpl;
import railo.runtime.net.ftp.FTPConnection;
import railo.runtime.net.ftp.FTPConnectionImpl;
import railo.runtime.net.ftp.FTPConstant;
import railo.runtime.net.ftp.FTPPath;
import railo.runtime.net.ftp.FTPPool;
import railo.runtime.op.Caster;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.QueryImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.dt.DateTimeImpl;
import railo.runtime.type.util.ListUtil;
/**
*
* Lets users implement File Transfer Protocol (FTP) operations.
*
*
*
**/
public final class Ftp extends TagImpl {
private static final String ASCCI_EXT_LIST="txt;htm;html;cfm;cfml;shtm;shtml;css;asp;asa";
private static final Key SUCCEEDED = KeyImpl.intern("succeeded");
private static final Key ERROR_CODE = KeyImpl.intern("errorCode");
private static final Key ERROR_TEXT = KeyImpl.intern("errorText");
private static final Key RETURN_VALUE = KeyImpl.intern("returnValue");
private static final Key CFFTP = KeyImpl.intern("cfftp");
/*private static final Key = KeyImpl.getInstance();
private static final Key = KeyImpl.getInstance();
private static final Key = KeyImpl.getInstance();
private static final Key = KeyImpl.getInstance();
private static final Key = KeyImpl.getInstance();
private static final Key = KeyImpl.getInstance();*/
private FTPPool pool;
private String action;
private String username;
private String password;
private String server;
private int timeout=30;
private int port=21;
private String connectionName;
private int retrycount=1;
private int count=0;
private boolean stoponerror=true;
private boolean passive;
private String name;
private String directory;
private String ASCIIExtensionList=ASCCI_EXT_LIST;
private short transferMode=FTPConstant.TRANSFER_MODE_AUTO;
private String remotefile;
private String localfile;
private boolean failifexists=true;
private String existing;
private String _new;
private String item;
private String result;
private String proxyserver;
private int proxyport=80;
private String proxyuser;
private String proxypassword="";
//private Struct cfftp=new StructImpl();
@Override
public void release() {
super.release();
this.pool=null;
this.action=null;
this.username=null;
this.password=null;
this.server=null;
this.timeout=30;
this.port=21;
this.connectionName=null;
this.proxyserver=null;
this.proxyport=80;
this.proxyuser=null;
this.proxypassword="";
this.retrycount=1;
this.count=0;
this.stoponerror=true;
this.passive=false;
this.name=null;
this.directory=null;
this.ASCIIExtensionList=ASCCI_EXT_LIST;
this.transferMode=FTPConstant.TRANSFER_MODE_AUTO;
this.remotefile=null;
this.localfile=null;
this.failifexists=true;
this.existing=null;
this._new=null;
this.item=null;
this.result=null;
}
/**
* sets the attribute action
* @param action
*/
public void setAction(String action) {
this.action=action.trim().toLowerCase();
}
@Override
public int doStartTag() {
return SKIP_BODY;
}
@Override
public int doEndTag() throws PageException {
pool=pageContext.getFTPPool();
FTPClient client = null;
// retries
do {
try {
if(action.equals("open")) client=actionOpen();
else if(action.equals("close")) client=actionClose();
else if(action.equals("changedir")) client=actionChangeDir();
else if(action.equals("createdir")) client=actionCreateDir();
else if(action.equals("listdir")) client=actionListDir();
else if(action.equals("removedir")) client=actionRemoveDir();
else if(action.equals("getfile")) client=actionGetFile();
else if(action.equals("putfile")) client=actionPutFile();
else if(action.equals("rename")) client=actionRename();
else if(action.equals("remove")) client=actionRemove();
else if(action.equals("getcurrentdir")) client=actionGetCurrentDir();
else if(action.equals("getcurrenturl")) client=actionGetCurrentURL();
else if(action.equals("existsdir")) client=actionExistsDir();
else if(action.equals("existsfile")) client=actionExistsFile();
else if(action.equals("exists")) client=actionExists();
//else if(action.equals("copy")) client=actionCopy();
else throw new ApplicationException(
"attribute action has an invalid value ["+action+"]",
"valid values are [open,close,listDir,createDir,removeDir,changeDir,getCurrentDir," +
"getCurrentURL,existsFile,existsDir,exists,getFile,putFile,rename,remove]");
}
catch(IOException ioe) {
if(count++<retrycount)continue;
throw Caster.toPageException(ioe);
}
if(client==null || !checkCompletion(client))break;
}while(true);
return EVAL_PAGE;
}
/**
* check if a file or directory exists
* @return FTPCLient
* @throws PageException
* @throws IOException
*/
private FTPClient actionExists() throws PageException, IOException {
required("item",item);
FTPClient client = getClient();
FTPFile file=existsFile(client,item,false);
Struct cfftp = writeCfftp(client);
cfftp.setEL(RETURN_VALUE,Caster.toBoolean(file!=null));
cfftp.setEL(SUCCEEDED,Boolean.TRUE);
return client;
}
/**
* check if a directory exists or not
* @return FTPCLient
* @throws PageException
* @throws IOException
*/
private FTPClient actionExistsDir() throws PageException, IOException {
required("directory",directory);
FTPClient client = getClient();
boolean res = existsDir(client,directory);
Struct cfftp = writeCfftp(client);
cfftp.setEL(RETURN_VALUE,Caster.toBoolean(res));
cfftp.setEL(SUCCEEDED,Boolean.TRUE);
stoponerror=false;
return client;
/*FTPClient client = pool.get(createConnection());
FTPFile file=existsFile(client,directory);
Struct cfftp = writeCfftp(client);
cfftp.setEL(RETURN_VALUE,Caster.toBoolean(file!=null && file.isDirectory()));
cfftp.setEL(SUCCEEDED,Boolean.TRUE);
stoponerror=false;
return client;*/
}
/**
* check if a file exists or not
* @return FTPCLient
* @throws IOException
* @throws PageException
*/
private FTPClient actionExistsFile() throws PageException, IOException {
required("remotefile",remotefile);
FTPClient client = getClient();
FTPFile file=existsFile(client,remotefile,true);
Struct cfftp = writeCfftp(client);
cfftp.setEL(RETURN_VALUE,Caster.toBoolean(file!=null && file.isFile()));
cfftp.setEL(SUCCEEDED,Boolean.TRUE);
stoponerror=false;
return client;
}
/* *
* check if file or directory exists if it exists return FTPFile otherwise null
* @param client
* @param strPath
* @return FTPFile or null
* @throws IOException
* @throws PageException
* /
private FTPFile exists(FTPClient client, String strPath) throws PageException, IOException {
strPath=strPath.trim();
// get parent path
FTPPath path=new FTPPath(client.printWorkingDirectory(),strPath);
String name=path.getName();
print.out("path:"+name);
// when directory
FTPFile[] files=null;
try {
files = client.listFiles(path.getPath());
} catch (IOException e) {}
if(files!=null) {
for(int i=0;i<files.length;i++) {
if(files[i].getName().equalsIgnoreCase(name)) {
return files[i];
}
}
}
return null;
}*/
private FTPFile existsFile(FTPClient client, String strPath,boolean isFile) throws PageException, IOException {
strPath=strPath.trim();
if(strPath.equals("/")) {
FTPFile file= new FTPFile();
file.setName("/");
file.setType(FTPFile.DIRECTORY_TYPE);
return file;
}
// get parent path
FTPPath path=new FTPPath(client.printWorkingDirectory(),strPath);
String p=path.getPath();
String n=path.getName();
strPath=p;
if("//".equals(p))strPath="/";
if(isFile)strPath+=n;
// when directory
FTPFile[] files=null;
try {
files = client.listFiles(strPath);
} catch (IOException e) {}
if(files!=null) {
for(int i=0;i<files.length;i++) {
if(files[i].getName().equalsIgnoreCase(n)) {
return files[i];
}
}
}
return null;
}
private boolean existsDir(FTPClient client, String strPath) throws PageException, IOException {
strPath=strPath.trim();
// get parent path
FTPPath path=new FTPPath(client.printWorkingDirectory(),strPath);
String p=path.getPath();
String n=path.getName();
strPath=p+""+n;
if("//".equals(p))strPath="/"+n;
if(!strPath.endsWith("/"))strPath+="/";
String pwd = client.printWorkingDirectory();
boolean rc = client.changeWorkingDirectory(directory);
client.changeWorkingDirectory(pwd);
return rc;
}
/**
* removes a file on the server
* @return FTPCLient
* @throws IOException
* @throws PageException
*/
private FTPClient actionRemove() throws IOException, PageException {
required("item",item);
FTPClient client = getClient();
client.deleteFile(item);
writeCfftp(client);
return client;
}
/**
* rename a file on the server
* @return FTPCLient
* @throws PageException
* @throws IOException
*/
private FTPClient actionRename() throws PageException, IOException {
required("existing",existing);
required("new",_new);
FTPClient client = getClient();
client.rename(existing,_new);
writeCfftp(client);
return client;
}
/**
* copy a local file to server
* @return FTPClient
* @throws IOException
* @throws PageException
*/
private FTPClient actionPutFile() throws IOException, PageException {
required("remotefile",remotefile);
required("localfile",localfile);
FTPClient client = getClient();
Resource local=ResourceUtil.toResourceExisting(pageContext ,localfile);//new File(localfile);
// if(failifexists && local.exists()) throw new ApplicationException("File ["+local+"] already exist, if you want to overwrite, set attribute failIfExists to false");
InputStream is=null;
try {
is=IOUtil.toBufferedInputStream(local.getInputStream());
client.setFileType(getType(local));
client.storeFile(remotefile,is);
}
finally {
IOUtil.closeEL(is);
}
writeCfftp(client);
return client;
}
/**
* gets a file from server and copy it local
* @return FTPCLient
* @throws PageException
* @throws IOException
*/
private FTPClient actionGetFile() throws PageException, IOException {
required("remotefile",remotefile);
required("localfile",localfile);
FTPClient client = getClient();
Resource local=ResourceUtil.toResourceExistingParent(pageContext ,localfile);//new File(localfile);
pageContext.getConfig().getSecurityManager().checkFileLocation(local);
if(failifexists && local.exists()) throw new ApplicationException("File ["+local+"] already exist, if you want to overwrite, set attribute failIfExists to false");
OutputStream fos=null;
client.setFileType(getType(local));
try {
fos=IOUtil.toBufferedOutputStream(local.getOutputStream());
client.retrieveFile(remotefile,fos);
}
finally {
IOUtil.closeEL(fos);
}
writeCfftp(client);
return client;
}
/**
* get url of the working directory
* @return FTPCLient
* @throws IOException
* @throws PageException
*/
private FTPClient actionGetCurrentURL() throws PageException, IOException {
FTPClient client = getClient();
String pwd=client.printWorkingDirectory();
Struct cfftp = writeCfftp(client);
cfftp.setEL("returnValue","ftp://"+client.getRemoteAddress().getHostName()+pwd);
return client;
}
/**
* get path from the working directory
* @return FTPCLient
* @throws IOException
* @throws PageException
*/
private FTPClient actionGetCurrentDir() throws PageException, IOException {
FTPClient client = getClient();
String pwd=client.printWorkingDirectory();
Struct cfftp = writeCfftp(client);
cfftp.setEL("returnValue",pwd);
return client;
}
/**
* change working directory
* @return FTPCLient
* @throws IOException
* @throws PageException
*/
private FTPClient actionChangeDir() throws IOException, PageException {
required("directory",directory);
FTPClient client = getClient();
client.changeWorkingDirectory(directory);
writeCfftp(client);
return client;
}
private FTPClient getClient() throws PageException, IOException {
return pool.get(_createConnection());
}
/**
* removes a remote directory on server
* @return FTPCLient
* @throws IOException
* @throws PageException
*/
private FTPClient actionRemoveDir() throws IOException, PageException {
required("directory",directory);
FTPClient client = getClient();
client.removeDirectory(directory);
writeCfftp(client);
return client;
}
/**
* create a remote directory
* @return FTPCLient
* @throws IOException
* @throws PageException
*/
private FTPClient actionCreateDir() throws IOException, PageException {
required("directory",directory);
FTPClient client = getClient();
client.makeDirectory(directory);
writeCfftp(client);
return client;
}
/**
* List data of a ftp connection
* @return FTPCLient
* @throws PageException
* @throws IOException
*/
private FTPClient actionListDir() throws PageException, IOException {
required("name",name);
required("directory",directory);
FTPClient client = getClient();
FTPFile[] files = client.listFiles(directory);
if(files==null)files=new FTPFile[0];
String[] cols = new String[]{"attributes","isdirectory","lastmodified","length","mode","name",
"path","url","type","raw"};
String[] types = new String[]{"VARCHAR","BOOLEAN","DATE","DOUBLE","VARCHAR","VARCHAR",
"VARCHAR","VARCHAR","VARCHAR","VARCHAR"};
railo.runtime.type.Query query=new QueryImpl(cols,types,0,"query");
// translate directory path for display
if(directory.length()==0)directory="/";
else if(directory.startsWith("./"))directory=directory.substring(1);
else if(directory.charAt(0)!='/')directory='/'+directory;
if(directory.charAt(directory.length()-1)!='/')directory=directory+'/';
pageContext.setVariable(name,query);
int row=0;
for(int i=0;i<files.length;i++) {
FTPFile file = files[i];
if(file.getName().equals(".") || file.getName().equals("..")) continue;
query.addRow();
row++;
query.setAt("attributes",row,"");
query.setAt("isdirectory",row,Caster.toBoolean(file.isDirectory()));
query.setAt("lastmodified",row,new DateTimeImpl(file.getTimestamp()));
query.setAt("length",row,Caster.toDouble(file.getSize()));
query.setAt("mode",row,FTPConstant.getPermissionASInteger(file));
query.setAt("type",row,FTPConstant.getTypeAsString(file.getType()));
//query.setAt("permission",row,FTPConstant.getPermissionASInteger(file));
query.setAt("raw",row,file.getRawListing());
query.setAt("name",row,file.getName());
query.setAt("path",row,directory+file.getName());
query.setAt("url",row,"ftp://"+client.getRemoteAddress().getHostName()+""+directory+file.getName());
}
writeCfftp(client);
return client;
}
/**
* Opens a FTP Connection
* @return FTPCLinet
* @throws IOException
* @throws PageException
*/
private FTPClient actionOpen() throws IOException, PageException {
required("server",server);
required("username",username);
required("password",password);
FTPClient client = getClient();
writeCfftp(client);
return client;
}
/**
* close a existing ftp connection
* @return FTPCLient
* @throws PageException
*/
private FTPClient actionClose() throws PageException {
FTPConnection conn = _createConnection();
FTPClient client = pool.remove(conn);
Struct cfftp = writeCfftp(client);
cfftp.setEL("succeeded",Caster.toBoolean(client!=null));
return client;
}
/**
* throw a error if the value is empty (null)
* @param attributeName
* @param atttributValue
* @throws ApplicationException
*/
private void required(String attributeName, String atttributValue) throws ApplicationException {
if(atttributValue==null)
throw new ApplicationException(
"invalid attribute constelation for the tag ftp",
"attribute ["+attributeName+"] is required, if action is ["+action+"]");
}
/**
* writes cfftp variable
* @param client
* @return FTPCLient
* @throws PageException
*/
private Struct writeCfftp(FTPClient client) throws PageException {
Struct cfftp=new StructImpl();
if(result==null)pageContext.variablesScope().setEL(CFFTP,cfftp);
else pageContext.setVariable(result,cfftp);
if(client==null) {
cfftp.setEL(SUCCEEDED,Boolean.FALSE);
cfftp.setEL(ERROR_CODE,new Double(-1));
cfftp.setEL(ERROR_TEXT,"");
cfftp.setEL(RETURN_VALUE,"");
return cfftp;
}
int repCode = client.getReplyCode();
String repStr=client.getReplyString();
cfftp.setEL(ERROR_CODE,new Double(repCode));
cfftp.setEL(ERROR_TEXT,repStr);
cfftp.setEL(SUCCEEDED,Caster.toBoolean(FTPReply.isPositiveCompletion(repCode)));
cfftp.setEL(RETURN_VALUE,repStr);
return cfftp;
}
/**
* check completion status of the client
* @param client
* @return FTPCLient
* @throws ApplicationException
*/
private boolean checkCompletion(FTPClient client) throws ApplicationException {
boolean isPositiveCompletion=FTPReply.isPositiveCompletion(client.getReplyCode());
if(isPositiveCompletion) return false;
if(count++<retrycount) return true;
if(stoponerror){
throw new railo.runtime.exp.FTPException(action,client);
}
return false;
}
/**
* get FTP. ... _FILE_TYPE
* @param file
* @return type
*/
private int getType(Resource file) {
if(transferMode==FTPConstant.TRANSFER_MODE_BINARY) return FTP.BINARY_FILE_TYPE;
else if(transferMode==FTPConstant.TRANSFER_MODE_ASCCI) return FTP.ASCII_FILE_TYPE;
else {
String ext=ResourceUtil.getExtension(file,null);
if(ext==null || ListUtil.listContainsNoCase(ASCIIExtensionList,ext,";")==-1)
return FTP.BINARY_FILE_TYPE;
return FTP.ASCII_FILE_TYPE;
}
}
/**
* @return return a new FTP Connection Object
*/
private FTPConnection _createConnection() {
return new FTPConnectionImpl(connectionName,server,username,password,port,timeout,transferMode,passive,proxyserver,proxyport,proxyuser,proxypassword);
}
/**
* @param password The password to set.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @param username The username to set.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* @param server The server to set.
*/
public void setServer(String server) {
this.server = server;
}
/**
* @param timeout The timeout to set.
*/
public void setTimeout(double timeout) {
this.timeout = (int)timeout;
}
/**
* @param port The port to set.
*/
public void setPort(double port) {
this.port = (int)port;
}
/**
* @param connection The connection to set.
*/
public void setConnection(String connection) {
this.connectionName = connection;
}
/**
* @param proxyserver The proxyserver to set.
*/
public void setProxyserver(String proxyserver) {
this.proxyserver = proxyserver;
}
/** set the value proxyport
* The port number on the proxy server from which the object is requested. Default is 80. When
* used with resolveURL, the URLs of retrieved documents that specify a port number are automatically
* resolved to preserve links in the retrieved document.
* @param proxyport value to set
**/
public void setProxyport(double proxyport) {
this.proxyport=(int)proxyport;
}
/** set the value username
* When required by a proxy server, a valid username.
* @param proxyuser value to set
**/
public void setProxyuser(String proxyuser) {
this.proxyuser=proxyuser;
}
/** set the value password
* When required by a proxy server, a valid password.
* @param proxypassword value to set
**/
public void setProxypassword(String proxypassword) {
this.proxypassword=proxypassword;
}
/**
* @param retrycount The retrycount to set.
*/
public void setRetrycount(double retrycount) {
this.retrycount = (int)retrycount;
}
/**
* @param stoponerror The stoponerror to set.
*/
public void setStoponerror(boolean stoponerror) {
this.stoponerror = stoponerror;
}
/**
* @param passive The passive to set.
*/
public void setPassive(boolean passive) {
this.passive = passive;
}
/**
* @param directory The directory to set.
*/
public void setDirectory(String directory) {
this.directory = directory;
}
/**
* @param name The name to set.
*/
public void setName(String name) {
this.name = name;
}
/**
* @param extensionList The aSCIIExtensionList to set.
*/
public void setAsciiextensionlist(String extensionList) {
ASCIIExtensionList = extensionList.toLowerCase().trim();
}
/**
* @param transferMode The transferMode to set.
*/
public void setTransfermode(String transferMode) {
transferMode=transferMode.toLowerCase().trim();
if(transferMode.equals("binary"))this.transferMode=FTPConstant.TRANSFER_MODE_BINARY;
else if(transferMode.equals("ascci"))this.transferMode=FTPConstant.TRANSFER_MODE_ASCCI;
else this.transferMode=FTPConstant.TRANSFER_MODE_AUTO;
}
/**
* @param localfile The localfile to set.
*/
public void setLocalfile(String localfile) {
this.localfile = localfile;
}
/**
* @param remotefile The remotefile to set.
*/
public void setRemotefile(String remotefile) {
this.remotefile = remotefile;
}
/**
* @param failifexists The failifexists to set.
*/
public void setFailifexists(boolean failifexists) {
this.failifexists = failifexists;
}
/**
* @param _new The _new to set.
*/
public void setNew(String _new) {
this._new = _new;
}
/**
* @param existing The existing to set.
*/
public void setExisting(String existing) {
this.existing = existing;
}
/**
* @param item The item to set.
*/
public void setItem(String item) {
this.item = item;
}
/**
* @param result The result to set.
*/
public void setResult(String result) {
this.result = result;
}
}