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