package railo.runtime.tag; import static railo.runtime.tag.util.FileUtil.NAMECONFLICT_ERROR; import static railo.runtime.tag.util.FileUtil.NAMECONFLICT_MAKEUNIQUE; import static railo.runtime.tag.util.FileUtil.NAMECONFLICT_OVERWRITE; import static railo.runtime.tag.util.FileUtil.NAMECONFLICT_SKIP; import static railo.runtime.tag.util.FileUtil.NAMECONFLICT_UNDEFINED; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import railo.commons.io.CharsetUtil; import railo.commons.io.IOUtil; import railo.commons.io.ModeUtil; import railo.commons.io.SystemUtil; import railo.commons.io.res.Resource; import railo.commons.io.res.type.s3.S3; import railo.commons.io.res.type.s3.S3Resource; import railo.commons.io.res.util.ModeObjectWrap; import railo.commons.io.res.util.ResourceUtil; import railo.commons.lang.StringUtil; import railo.commons.lang.mimetype.MimeType; import railo.runtime.PageContext; import railo.runtime.exp.ApplicationException; import railo.runtime.exp.PageException; import railo.runtime.ext.tag.BodyTagImpl; import railo.runtime.functions.list.ListFirst; import railo.runtime.functions.list.ListLast; import railo.runtime.functions.s3.StoreSetACL; import railo.runtime.img.ImageUtil; import railo.runtime.op.Caster; import railo.runtime.op.Decision; import railo.runtime.tag.util.FileUtil; import railo.runtime.type.Array; import railo.runtime.type.ArrayImpl; import railo.runtime.type.Struct; import railo.runtime.type.StructImpl; import railo.runtime.type.dt.DateImpl; import railo.runtime.type.dt.DateTimeImpl; import railo.runtime.type.scope.Form; import railo.runtime.type.scope.FormItem; import railo.runtime.type.util.ArrayUtil; import railo.runtime.type.util.KeyConstants; import railo.runtime.type.util.ListUtil; /** * Handles all interactions with files. The attributes you use with cffile depend on the value of the action attribute. * For example, if the action = "write", use the attributes associated with writing a text file. * * * **/ public final class FileTag extends BodyTagImpl { private static final int ACTION_UNDEFINED = 0; private static final int ACTION_MOVE = 1; private static final int ACTION_WRITE = 2; private static final int ACTION_APPEND = 3; private static final int ACTION_READ = 4; private static final int ACTION_UPLOAD = 5; private static final int ACTION_UPLOAD_ALL = 6; private static final int ACTION_COPY = 7; private static final int ACTION_INFO = 8; private static final int ACTION_TOUCH = 9; private static final int ACTION_DELETE = 10; private static final int ACTION_READ_BINARY = 11; //private static final Key SET_ACL = KeyImpl.intern("setACL"); //private static final String DEFAULT_ENCODING=Charset.getDefault(); /** Type of file manipulation that the tag performs. */ private int action; /** Absolute pathname of directory or file on web server. */ private String strDestination; /** Content of the file to be created. */ private Object output; /** Absolute pathname of file on web server. */ private Resource file; /** Applies only to Solaris and HP-UX. Permissions. Octal values of UNIX chmod command. Assigned to owner, group, and other, respectively. */ private int mode=-1; /** Name of variable to contain contents of text file. */ private String variable; /** Name of form field used to select the file. */ private String filefield; /** Character set name for the file contents. */ private Charset charset=null; /** Yes: appends newline character to text written to file */ private boolean addnewline=true; private boolean fixnewline=true; /** One attribute (Windows) or a comma-delimited list of attributes (other platforms) to set on the file. ** If omitted, the file's attributes are maintained. */ private String attributes; /** Absolute pathname of file on web server. ** On Windows, use backward slashes; on UNIX, use forward slashes. */ private Resource source; /** Action to take if filename is the same as that of a file in the directory. */ private int nameconflict=NAMECONFLICT_UNDEFINED; /** Limits the MIME types to accept. Comma-delimited list. For example, to permit JPG and Microsoft Word file uploads: ** accept = "image/jpg, application/msword" ** The browser uses file extension to determine file type. */ private String accept; private boolean strict=true; private boolean createPath=false; private String result=null; private railo.runtime.security.SecurityManager securityManager; private String serverPassword=null; private Object acl=null; @Override public void release() { super.release(); acl=null; action=ACTION_UNDEFINED; strDestination=null; output=null; file=null; mode=-1; variable=null; filefield=null; charset=null; addnewline=true; fixnewline=true; attributes=null; source=null; nameconflict=NAMECONFLICT_UNDEFINED; accept=null; strict=true; createPath=false; securityManager=null; result=null; serverPassword=null; } /** set the value action * Type of file manipulation that the tag performs. * @param strAction value to set **/ public void setAction(String strAction) throws ApplicationException { strAction=strAction.toLowerCase(); if(strAction.equals("move") || strAction.equals("rename")) action=ACTION_MOVE; else if(strAction.equals("copy")) action=ACTION_COPY; else if(strAction.equals("delete")) action=ACTION_DELETE; else if(strAction.equals("read")) action=ACTION_READ; else if(strAction.equals("readbinary")) action=ACTION_READ_BINARY; else if(strAction.equals("write")) action=ACTION_WRITE; else if(strAction.equals("append")) action=ACTION_APPEND; else if(strAction.equals("upload")) action=ACTION_UPLOAD; else if(strAction.equals("uploadall")) action=ACTION_UPLOAD_ALL; else if(strAction.equals("info")) action=ACTION_INFO; else if(strAction.equals("touch")) action=ACTION_TOUCH; else throw new ApplicationException("invalid value ["+strAction+"] for attribute action","values for attribute action are:info,move,rename,copy,delete,read,readbinary,write,append,upload,uploadall,touch"); } /** set the value destination * Absolute pathname of directory or file on web server. * @param destination value to set **/ public void setDestination(String destination) { this.strDestination=destination;//ResourceUtil.toResourceNotExisting(pageContext ,destination); } /** set the value output * Content of the file to be created. * @param output value to set **/ public void setOutput(Object output) { if(output==null)this.output=""; else this.output=output; } /** set the value file * Absolute pathname of file on web server. * @param file value to set **/ public void setFile(String file) { this.file=ResourceUtil.toResourceNotExisting(pageContext ,file); } /** set the value mode * Applies only to Solaris and HP-UX. Permissions. Octal values of UNIX chmod command. Assigned to owner, group, and other, respectively. * @param mode value to set * @throws PageException **/ public void setMode(String mode) throws PageException { this.mode=toMode(mode); } public static int toMode(String mode) throws PageException { if(StringUtil.isEmpty(mode,true)) return -1; try { return ModeUtil.toOctalMode(mode); } catch (IOException e) { throw Caster.toPageException(e); } } /** set the value variable * Name of variable to contain contents of text file. * @param variable value to set **/ public void setVariable(String variable) { this.variable=variable; } /** set the value filefield * Name of form field used to select the file. * @param filefield value to set **/ public void setFilefield(String filefield) { this.filefield=filefield; } /** set the value charset * Character set name for the file contents. * @param charset value to set **/ public void setCharset(String charset) { if(StringUtil.isEmpty(charset)) return; this.charset=CharsetUtil.toCharset(charset.trim()); } /** set the value acl * used only for s3 resources, for all others ignored * @param acl value to set * @throws ApplicationException * @Deprecated only exists for backward compatibility to old ra files. **/ public void setAcl(String acl) throws ApplicationException { this.acl=acl; } public void setAcl(Object acl) { this.acl=acl; } public void setStoreacl(Object acl) { this.acl=acl; } public void setServerpassword(String serverPassword) { this.serverPassword=serverPassword; } /** set the value addnewline * Yes: appends newline character to text written to file * @param addnewline value to set **/ public void setAddnewline(boolean addnewline) { this.addnewline=addnewline; } /** set the value attributes * One attribute (Windows) or a comma-delimited list of attributes (other platforms) to set on the file. * If omitted, the file's attributes are maintained. * @param attributes value to set **/ public void setAttributes(String attributes) { this.attributes=attributes; } /** set the value source * Absolute pathname of file on web server. * On Windows, use backward slashes; on UNIX, use forward slashes. * @param source value to set **/ public void setSource(String source) { this.source=ResourceUtil.toResourceNotExisting(pageContext ,source); } /** set the value nameconflict * Action to take if filename is the same as that of a file in the directory. * @param nameconflict value to set * @throws ApplicationException **/ public void setNameconflict(String nameconflict) throws ApplicationException { this.nameconflict = FileUtil.toNameConflict( nameconflict ); } /** set the value accept * Limits the MIME types to accept. Comma-delimited list. For example, to permit JPG and Microsoft Word file uploads: * accept = "image/jpg, application/msword" * The browser uses file extension to determine file type. * @param accept value to set **/ public void setAccept(String accept) { this.accept=accept; } public void setStrict(boolean strict) { this.strict=strict; } public void setCreatepath(boolean createPath) { this.createPath=createPath; } /** * @param result The result to set. */ public void setResult(String result) { this.result = result; } @Override public int doStartTag() throws PageException { if(charset==null) charset=CharsetUtil.toCharset(pageContext.getConfig().getResourceCharset()); // TODO 4.2 securityManager = pageContext.getConfig().getSecurityManager(); switch(action){ case ACTION_MOVE: actionMove(pageContext, securityManager,source, strDestination, nameconflict,serverPassword,acl, mode, attributes); break; case ACTION_COPY: actionCopy(pageContext, securityManager,source, strDestination, nameconflict,serverPassword,acl, mode, attributes); break; case ACTION_DELETE: actionDelete(); break; case ACTION_READ: actionRead(); break; case ACTION_READ_BINARY: actionReadBinary(); break; case ACTION_UPLOAD: actionUpload(); break; case ACTION_UPLOAD_ALL: actionUploadAll(); break; case ACTION_INFO: actionInfo(); break; case ACTION_TOUCH: actionTouch(); break; case ACTION_UNDEFINED: throw new ApplicationException("missing attribute action"); // should never happens // write and append default: return EVAL_BODY_BUFFERED; } return SKIP_BODY; } @Override public int doAfterBody() throws ApplicationException { if(action==ACTION_APPEND || action==ACTION_WRITE) { String body = bodyContent.getString(); if(!StringUtil.isEmpty(body)){ if(!StringUtil.isEmpty(output)) throw new ApplicationException("if a body is defined for the tag, the attribute [output] is not allowed"); output=body; } } return SKIP_BODY; } @Override public int doEndTag() throws PageException { switch(action){ case ACTION_APPEND: actionAppend(); break; case ACTION_WRITE: actionWrite(); break; } return EVAL_PAGE; } public void hasBody(boolean hasBody) { if(output==null && hasBody) output=""; } /** * move source file to destination path or file * @throws PageException */ public static void actionMove(PageContext pageContext, railo.runtime.security.SecurityManager securityManager, Resource source, String strDestination, int nameconflict,String serverPassword, Object acl, int mode, String attributes) throws PageException { if(nameconflict==NAMECONFLICT_UNDEFINED) nameconflict=NAMECONFLICT_OVERWRITE; if(source==null) throw new ApplicationException("attribute source is not defined for tag file"); if(StringUtil.isEmpty(strDestination)) throw new ApplicationException("attribute destination is not defined for tag file"); Resource destination=toDestination(pageContext,strDestination,source); securityManager.checkFileLocation(pageContext.getConfig(),source,serverPassword); securityManager.checkFileLocation(pageContext.getConfig(),destination,serverPassword); if(source.equals(destination)) return ; // source if(!source.exists()) throw new ApplicationException("source file ["+source.toString()+"] doesn't exist"); else if(!source.isFile()) throw new ApplicationException("source file ["+source.toString()+"] is not a file"); else if(!source.isReadable() || !source.isWriteable()) throw new ApplicationException("no access to source file ["+source.toString()+"]"); // destination if(destination.isDirectory()) destination=destination.getRealResource(source.getName()); if(destination.exists()) { // SKIP if(nameconflict==NAMECONFLICT_SKIP) return; // OVERWRITE else if(nameconflict==NAMECONFLICT_OVERWRITE) destination.delete(); // MAKEUNIQUE else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) destination=makeUnique(destination); // ERROR else throw new ApplicationException("destiniation file ["+destination.toString()+"] already exist"); } try { source.moveTo(destination); } catch(Throwable t) {t.printStackTrace(); throw new ApplicationException(t.getMessage()); } setACL(destination,acl); setMode(destination,mode); setAttributes(destination,attributes); } private static Resource toDestination(PageContext pageContext,String path, Resource source) { if(source!=null && path.indexOf(File.separatorChar)==-1 && path.indexOf('/')==-1 && path.indexOf('\\')==-1) { Resource p = source.getParentResource(); if(p!=null)return p.getRealResource(path); } return ResourceUtil.toResourceNotExisting(pageContext ,path); } /** * copy source file to destination file or path * @throws PageException */ public static void actionCopy(PageContext pageContext, railo.runtime.security.SecurityManager securityManager, Resource source, String strDestination, int nameconflict,String serverPassword, Object acl, int mode, String attributes) throws PageException { if(nameconflict==NAMECONFLICT_UNDEFINED) nameconflict=NAMECONFLICT_OVERWRITE; if(source==null) throw new ApplicationException("attribute source is not defined for tag file"); if(StringUtil.isEmpty(strDestination)) throw new ApplicationException("attribute destination is not defined for tag file"); Resource destination=toDestination(pageContext,strDestination,source); securityManager.checkFileLocation(pageContext.getConfig(),source,serverPassword); securityManager.checkFileLocation(pageContext.getConfig(),destination,serverPassword); // source if(!source.exists()) throw new ApplicationException("source file ["+source.toString()+"] doesn't exist"); else if(!source.isFile()) throw new ApplicationException("source file ["+source.toString()+"] is not a file"); else if(!source.canRead()) throw new ApplicationException("no access to source file ["+source.toString()+"]"); // destination if(destination.isDirectory()) destination=destination.getRealResource(source.getName()); if(destination.exists()) { // SKIP if(nameconflict==NAMECONFLICT_SKIP) return; // SKIP else if(nameconflict==NAMECONFLICT_OVERWRITE) destination.delete(); // MAKEUNIQUE else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) destination=makeUnique(destination); // ERROR else throw new ApplicationException("destiniation file ["+destination.toString()+"] already exist"); } try { IOUtil.copy(source,destination); } catch(IOException e) { ApplicationException ae = new ApplicationException("can't copy file ["+source+"] to ["+destination+"]",e.getMessage()); ae.setStackTrace(e.getStackTrace()); throw ae; } setACL(destination,acl); setMode(destination,mode); setAttributes(destination,attributes); } private static void setACL(Resource res,Object acl) throws PageException { String scheme = res.getResourceProvider().getScheme(); if("s3".equalsIgnoreCase(scheme)){ S3Resource s3r=(S3Resource) res; if(acl!=null){ try { // old way if(Decision.isString(acl)) { s3r.setACL(S3.toIntACL(Caster.toString(acl))); } // new way else { StoreSetACL.invoke(s3r, acl); } } catch (IOException e) { throw Caster.toPageException(e); } } } // set acl for s3 resource /*if(res instanceof S3Resource) { ((S3Resource)res).setACL(acl); }*/ } private static Resource makeUnique(Resource res) { String ext=getFileExtension(res); String name=getFileName(res); ext=(ext==null)?"":"."+ext; int count=0; while(res.exists()) { res=res.getParentResource().getRealResource(name+(++count)+ext); } return res; } /** * copy source file to destination file or path * @throws PageException */ private void actionDelete() throws PageException { checkFile(false,false,false,false); setACL(file,acl); try { if(!file.delete()) throw new ApplicationException("can't delete file ["+file+"]"); } catch(Throwable t) { throw new ApplicationException(t.getMessage()); } } /** * read source file * @throws PageException */ private void actionRead() throws PageException { if(variable==null) throw new ApplicationException("attribute variable is not defined for tag file"); checkFile(false,false,true,false); //print.ln(charset); //TextFile tf=new TextFile(file.getAbsolutePath()); try { pageContext.setVariable(variable,IOUtil.toString(file,charset)); } catch (IOException e) { throw new ApplicationException("can't read file ["+file+"]",e.getMessage()); } } /** * read source file * @throws PageException */ private void actionReadBinary() throws PageException { if(variable==null) throw new ApplicationException("attribute variable is not defined for tag file"); checkFile(false,false,true,false); //TextFile tf=new TextFile(file.getAbsolutePath()); try { pageContext.setVariable(variable,IOUtil.toBytes(file)); }catch (IOException e) { throw new ApplicationException("can't read binary file ["+source.toString()+"]",e.getMessage()); } } /** * write to the source file * @throws PageException */ private void actionWrite() throws PageException { if(output==null) throw new ApplicationException("attribute output is not defined for tag file"); checkFile(createPath,true,false,true); try { if(output instanceof InputStream) { IOUtil.copy( (InputStream)output, file, false); } else if(Decision.isCastableToBinary(output,false)) { IOUtil.copy( new ByteArrayInputStream(Caster.toBinary(output)), file, true); } else { String content=Caster.toString(output); if(fixnewline)content=doFixNewLine(content); if(addnewline) content+=SystemUtil.getOSSpecificLineSeparator(); IOUtil.write(file,content,charset,false); } } catch (UnsupportedEncodingException e) { throw new ApplicationException("Unsupported Charset Definition ["+charset+"]",e.getMessage()); } catch (IOException e) { throw new ApplicationException("can't write file "+file.getAbsolutePath(),e.getMessage()); } setACL(file,acl); setMode(file,mode); setAttributes(file,attributes); } /** * write to the source file * @throws PageException */ private void actionTouch() throws PageException { checkFile(createPath,true,true,true); try { ResourceUtil.touch(file); } catch (IOException e) { throw new ApplicationException("can't touch file "+file.getAbsolutePath(),e.getMessage()); } setACL(file,acl); setMode(file,mode); setAttributes(file,attributes); } /** * append data to source file * @throws PageException */ private void actionAppend() throws PageException { if(output==null) throw new ApplicationException("attribute output is not defined for tag file"); checkFile(createPath,true,false,true); try { if(!file.exists()) file.createNewFile(); String content=Caster.toString(output); if(fixnewline)content=doFixNewLine(content); if(addnewline) content+=SystemUtil.getOSSpecificLineSeparator(); IOUtil.write(file,content,charset,true); } catch (UnsupportedEncodingException e) { throw new ApplicationException("Unsupported Charset Definition ["+charset+"]",e.getMessage()); } catch (IOException e) { throw new ApplicationException("can't write file",e.getMessage()); } setACL(file,acl); setMode(file,mode); setAttributes(file,attributes); } private String doFixNewLine(String content) { // TODO replace new line with system new line return content; } /** * list all files and directories inside a directory * @throws PageException */ private void actionInfo() throws PageException { if(variable==null) throw new ApplicationException("attribute variable is not defined for tag file"); checkFile(false,false,false,false); Struct sct =new StructImpl(); pageContext.setVariable(variable,sct); // fill data to query sct.setEL(KeyConstants._name,file.getName()); sct.setEL(KeyConstants._size,Long.valueOf(file.length())); sct.setEL(KeyConstants._type,file.isDirectory()?"Dir":"File"); sct.setEL("dateLastModified",new DateTimeImpl(pageContext,file.lastModified(),false)); sct.setEL("attributes",getFileAttribute(file)); if(SystemUtil.isUnix())sct.setEL(KeyConstants._mode,new ModeObjectWrap(file)); try { BufferedImage bi = ImageUtil.toBufferedImage(file, null); if(bi!=null) { Struct img =new StructImpl(); img.setEL(KeyConstants._width,new Double(bi.getWidth())); img.setEL(KeyConstants._height,new Double(bi.getHeight())); sct.setEL(KeyConstants._img,img); } } catch (Throwable t) {} } private static String getFileAttribute(Resource file){ return file.exists() && !file.canWrite() ? "R".concat(file.isHidden() ? "H" : "") : file.isHidden() ? "H" : ""; } /** * read source file * @throws PageException */ public void actionUpload() throws PageException { FormItem item=getFormItem(pageContext,filefield); Struct cffile = _actionUpload(pageContext,securityManager,item,strDestination,nameconflict,accept,strict,mode,attributes,acl,serverPassword); if(StringUtil.isEmpty(result)) { pageContext.undefinedScope().set(KeyConstants._file,cffile); pageContext.undefinedScope().set("cffile",cffile); } else { pageContext.setVariable(result,cffile); } } public static Struct actionUpload(PageContext pageContext,railo.runtime.security.SecurityManager securityManager,String filefield, String strDestination,int nameconflict,String accept,boolean strict,int mode,String attributes,Object acl,String serverPassword) throws PageException { FormItem item=getFormItem(pageContext,filefield); return _actionUpload(pageContext,securityManager,item,strDestination,nameconflict,accept,strict,mode,attributes,acl,serverPassword); } public void actionUploadAll() throws PageException { Array arr=actionUploadAll(pageContext,securityManager,strDestination,nameconflict,accept,strict,mode,attributes,acl,serverPassword); if(StringUtil.isEmpty(result)) { Struct sct; if(arr!=null && arr.size()>0) sct=(Struct) arr.getE(1); else sct=new StructImpl(); pageContext.undefinedScope().set(KeyConstants._file,sct); pageContext.undefinedScope().set("cffile",sct); } else { pageContext.setVariable(result,arr); } } public static Array actionUploadAll(PageContext pageContext,railo.runtime.security.SecurityManager securityManager, String strDestination,int nameconflict,String accept,boolean strict,int mode,String attributes,Object acl,String serverPassword) throws PageException { FormItem[] items=getFormItems(pageContext); Struct sct=null; Array arr=new ArrayImpl(); for(int i=0;i<items.length;i++){ sct = _actionUpload(pageContext,securityManager,items[i],strDestination,nameconflict,accept,strict,mode,attributes,acl,serverPassword); arr.appendEL(sct); } return arr; } private static synchronized Struct _actionUpload(PageContext pageContext, railo.runtime.security.SecurityManager securityManager, FormItem formItem,String strDestination,int nameconflict,String accept,boolean strict,int mode,String attributes,Object acl,String serverPassword) throws PageException { if(nameconflict==NAMECONFLICT_UNDEFINED) nameconflict=NAMECONFLICT_ERROR; boolean fileWasRenamed=false; boolean fileWasAppended=false; boolean fileExisted=false; boolean fileWasOverwritten=false; String contentType=formItem.getContentType(); // set cffile struct Struct cffile=new StructImpl(); long length = formItem.getResource().length(); cffile.set("timecreated",new DateTimeImpl(pageContext.getConfig())); cffile.set("timelastmodified",new DateTimeImpl(pageContext.getConfig())); cffile.set("datelastaccessed",new DateImpl(pageContext)); cffile.set("oldfilesize",Long.valueOf(length)); cffile.set("filesize",Long.valueOf(length)); cffile.set("contenttype",ListFirst.call(pageContext,contentType,"/")); cffile.set("contentsubtype",ListLast.call(pageContext,contentType,"/")); // client file String strClientFile=formItem.getName(); while(strClientFile.indexOf('\\')!=-1) strClientFile=strClientFile.replace('\\','/'); Resource clientFile=pageContext.getConfig().getResource(strClientFile); String clientFileName=clientFile.getName(); // check file type checkContentType(contentType,accept,getFileExtension(clientFile),strict); //String dir=clientFile.getParent(); //dir=correctDirectory(dir); cffile.set("clientdirectory",getParent(clientFile)); cffile.set("clientfile",clientFile.getName()); cffile.set("clientfileext",getFileExtension(clientFile)); cffile.set("clientfilename",getFileName(clientFile)); // check destination if(StringUtil.isEmpty(strDestination)) throw new ApplicationException("attribute destination is not defined in tag file"); Resource destination=toDestination(pageContext,strDestination,null); securityManager.checkFileLocation(pageContext.getConfig(),destination,serverPassword); if(destination.isDirectory()) destination=destination.getRealResource(clientFileName); else if(!destination.exists() && (strDestination.endsWith("/") || strDestination.endsWith("\\"))) destination=destination.getRealResource(clientFileName); else if(!clientFileName.equalsIgnoreCase(destination.getName())) { if(ResourceUtil.getExtension(destination, null)==null) destination=destination.getRealResource(clientFileName); else fileWasRenamed=true; } // check parent destination -> directory of the desinatrion Resource parentDestination=destination.getParentResource(); if(!parentDestination.exists()) { Resource pp = parentDestination.getParentResource(); if(pp==null || !pp.exists()) throw new ApplicationException("attribute destination has an invalid value ["+destination+"], directory ["+parentDestination+"] doesn't exist"); try { parentDestination.createDirectory(true); } catch (IOException e) { throw Caster.toPageException(e); } } else if(!parentDestination.canWrite()) throw new ApplicationException("can't write to destination directory ["+parentDestination+"], no access to write"); // set server variables cffile.set("serverdirectory",getParent(destination)); cffile.set("serverfile",destination.getName()); cffile.set("serverfileext",getFileExtension(destination)); cffile.set("serverfilename",getFileName(destination)); cffile.set("attemptedserverfile",destination.getName()); // check nameconflict if(destination.exists()) { fileExisted=true; if(nameconflict==NAMECONFLICT_ERROR) { throw new ApplicationException("destination file ["+destination+"] already exist"); } else if(nameconflict==NAMECONFLICT_SKIP) { cffile.set("fileexisted",Caster.toBoolean(fileExisted)); cffile.set("filewasappended",Boolean.FALSE); cffile.set("filewasoverwritten",Boolean.FALSE); cffile.set("filewasrenamed",Boolean.FALSE); cffile.set("filewassaved",Boolean.FALSE); return cffile; } else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) { destination=makeUnique(destination); fileWasRenamed=true; //if(fileWasRenamed) { cffile.set("serverdirectory",getParent(destination)); cffile.set("serverfile",destination.getName()); cffile.set("serverfileext",getFileExtension(destination)); cffile.set("serverfilename",getFileName(destination)); cffile.set("attemptedserverfile",destination.getName()); //} } else if(nameconflict==NAMECONFLICT_OVERWRITE) { //fileWasAppended=true; fileWasOverwritten=true; if(!destination.delete()) if(destination.exists()) // hier hatte ich concurrent problem das damit ausgeraeumt ist throw new ApplicationException("can't delete destination file ["+destination+"]"); } // for "overwrite" no action is neded } try { destination.createNewFile(); IOUtil.copy(formItem.getResource(),destination); } catch(Throwable t) { throw Caster.toPageException(t); } // Set cffile/file struct cffile.set("fileexisted",Caster.toBoolean(fileExisted)); cffile.set("filewasappended",Caster.toBoolean(fileWasAppended)); cffile.set("filewasoverwritten",Caster.toBoolean(fileWasOverwritten)); cffile.set("filewasrenamed",Caster.toBoolean(fileWasRenamed)); cffile.set("filewassaved",Boolean.TRUE); setACL(destination,acl); setMode(destination,mode); setAttributes(destination, attributes); return cffile; } /** * check if the content ii ok * @param contentType * @throws PageException */ private static void checkContentType(String contentType,String accept,String ext,boolean strict) throws PageException { if(!StringUtil.isEmpty(ext,true)){ ext=ext.trim().toLowerCase(); if(ext.startsWith("*."))ext=ext.substring(2); if(ext.startsWith("."))ext=ext.substring(1); } else ext=null; if(StringUtil.isEmpty(accept,true)) return; MimeType mt = MimeType.getInstance(contentType),sub; Array whishedTypes=ListUtil.listToArrayRemoveEmpty(accept,','); int len=whishedTypes.size(); for(int i=1;i<=len;i++) { String whishedType=Caster.toString(whishedTypes.getE(i)).trim().toLowerCase(); if(whishedType.equals("*")) return; // check mimetype if(ListUtil.len(whishedType, "/", true)==2){ sub=MimeType.getInstance(whishedType); if(mt.match(sub)) return; } // check extension if(ext!=null && !strict){ if(whishedType.startsWith("*."))whishedType=whishedType.substring(2); if(whishedType.startsWith("."))whishedType=whishedType.substring(1); if(ext.equals(whishedType)) return; } } throw new ApplicationException("The MIME type of the uploaded file ["+contentType+"] was not accepted by the server.","only this ["+accept+"] mime type are accepted"); } /** * rreturn fileItem matching to filefiled definition or throw a exception * @return FileItem * @throws ApplicationException */ private static FormItem getFormItem(PageContext pageContext, String filefield) throws PageException { // check filefield if(StringUtil.isEmpty(filefield)){ FormItem[] items = getFormItems(pageContext); if(ArrayUtil.isEmpty(items)) throw new ApplicationException("no file send with this form"); return items[0]; } PageException pe = pageContext.formScope().getInitException(); if(pe!=null) throw pe; railo.runtime.type.scope.Form upload = pageContext.formScope(); FormItem fileItem = upload.getUploadResource(filefield); if(fileItem==null) { FormItem[] items = upload.getFileItems(); StringBuilder sb=new StringBuilder(); for(int i=0;i<items.length;i++){ if(i!=0) sb.append(", "); sb.append(items[i].getFieldName()); } String add="."; if(sb.length()>0) add=", valid field names are ["+sb+"]."; if(pageContext.formScope().get(filefield,null)==null) throw new ApplicationException("form field ["+filefield+"] is not a file field"+add); throw new ApplicationException("form field ["+filefield+"] doesn't exist or has no content"+add); } return fileItem; } private static FormItem[] getFormItems(PageContext pageContext) throws PageException { PageException pe = pageContext.formScope().getInitException(); if(pe!=null) throw pe; Form scope = pageContext.formScope(); return scope.getFileItems(); } /** * get file extension of a file object * @param file file object * @return extnesion */ private static String getFileExtension(Resource file) { String name=file.getName(); String[] arr; try { arr = ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(name, '.')); } catch (PageException e) { arr=null; } if(arr.length<2) return ""; return arr[arr.length-1]; } /** * get file name of a file object without extension * @param file file object * @return name of the file */ private static String getFileName(Resource file) { String name=file.getName(); int pos=name.lastIndexOf("."); if(pos==-1)return name; return name.substring(0,pos); } /*private String correctDirectory(Resource resource) { if(StringUtil.isEmpty(resource,true)) return ""; resource=resource.trim(); if((StringUtil.endsWith(resource, '/') || StringUtil.endsWith(resource, '\\')) && resource.length()>1) { return resource.substring(0,resource.length()-1); } return resource; }*/ private static String getParent(Resource res) { Resource parent = res.getParentResource(); //print.out("res:"+res); //print.out("parent:"+parent); if(parent==null) return ""; return ResourceUtil.getCanonicalPathEL(parent); } private void checkFile(boolean createParent, boolean create, boolean canRead, boolean canWrite) throws PageException { if(file==null) throw new ApplicationException("attribute file is not defined for tag file"); securityManager.checkFileLocation(pageContext.getConfig(),file,serverPassword); if(!file.exists()) { if(create) { Resource parent=file.getParentResource(); if(parent!=null && !parent.exists()) { if(createParent) parent.mkdirs(); else throw new ApplicationException("parent directory for ["+file+"] doesn't exist"); } try { file.createFile(false); } catch (IOException e) { throw new ApplicationException("invalid file ["+file+"]",e.getMessage()); } } else if(!file.isFile()) throw new ApplicationException("source file ["+file.toString()+"] is not a file"); else throw new ApplicationException("source file ["+file.toString()+"] doesn't exist"); } else if(!file.isFile()) throw new ApplicationException("source file ["+file.toString()+"] is not a file"); else if(canRead &&!file.canRead()) throw new ApplicationException("no read access to source file ["+file.toString()+"]"); else if(canWrite && !file.canWrite()) throw new ApplicationException("no write access to source file ["+file.toString()+"]"); } /** * set attributes on file * @param file * @throws PageException */ private static void setAttributes(Resource file,String attributes) throws PageException { if(!SystemUtil.isWindows() || StringUtil.isEmpty(attributes)) return; try { ResourceUtil.setAttribute(file, attributes); } catch (IOException e) { throw new ApplicationException("can't change attributes of file "+file,e.getMessage()); } } /** * change mode of given file * @param file * @throws ApplicationException */ private static void setMode(Resource file,int mode) throws ApplicationException { if(mode==-1 || SystemUtil.isWindows()) return; try { file.setMode(mode); //FileUtil.setMode(file,mode); } catch (IOException e) { throw new ApplicationException("can't change mode of file "+file,e.getMessage()); } } /** * @param fixnewline the fixnewline to set */ public void setFixnewline(boolean fixnewline) { this.fixnewline = fixnewline; } }