001    package railo.runtime.tag;
002    
003    import java.awt.image.BufferedImage;
004    import java.io.ByteArrayInputStream;
005    import java.io.File;
006    import java.io.IOException;
007    import java.io.InputStream;
008    import java.io.UnsupportedEncodingException;
009    
010    import railo.commons.io.IOUtil;
011    import railo.commons.io.ModeUtil;
012    import railo.commons.io.SystemUtil;
013    import railo.commons.io.res.Resource;
014    import railo.commons.io.res.type.s3.S3;
015    import railo.commons.io.res.type.s3.S3Resource;
016    import railo.commons.io.res.util.ModeObjectWrap;
017    import railo.commons.io.res.util.ResourceUtil;
018    import railo.commons.lang.StringUtil;
019    import railo.runtime.PageContext;
020    import railo.runtime.exp.ApplicationException;
021    import railo.runtime.exp.PageException;
022    import railo.runtime.ext.tag.TagImpl;
023    import railo.runtime.functions.list.ListFirst;
024    import railo.runtime.functions.list.ListLast;
025    import railo.runtime.functions.s3.StoreSetACL;
026    import railo.runtime.img.ImageUtil;
027    import railo.runtime.op.Caster;
028    import railo.runtime.op.Decision;
029    import railo.runtime.type.Array;
030    import railo.runtime.type.ArrayImpl;
031    import railo.runtime.type.Collection.Key;
032    import railo.runtime.type.KeyImpl;
033    import railo.runtime.type.List;
034    import railo.runtime.type.Struct;
035    import railo.runtime.type.StructImpl;
036    import railo.runtime.type.dt.DateImpl;
037    import railo.runtime.type.dt.DateTimeImpl;
038    import railo.runtime.type.scope.FormImpl;
039    import railo.runtime.type.scope.FormImpl.Item;
040    import railo.runtime.type.scope.FormUpload;
041    import railo.runtime.type.util.ArrayUtil;
042    
043    /**
044    * Handles all interactions with files. The attributes you use with cffile depend on the value of the action attribute. 
045    *  For example, if the action = "write", use the attributes associated with writing a text file.
046    *
047    *
048    *
049    **/
050    public final class FileTag extends TagImpl {
051    
052            public static final int NAMECONFLICT_UNDEFINED=0;
053            public static final int NAMECONFLICT_ERROR=1;
054            public static final int NAMECONFLICT_SKIP=2;
055            public static final int NAMECONFLICT_OVERWRITE=3;
056            public static final int NAMECONFLICT_MAKEUNIQUE=4;
057            private static final Key SET_ACL = KeyImpl.intern("setACL");
058        
059        //private static final String DEFAULT_ENCODING=Charset.getDefault();
060    
061            /** Type of file manipulation that the tag performs. */
062            private String action;
063    
064            /** Absolute pathname of directory or file on web server. */
065            private String strDestination;
066    
067            /** Content of the file to be created. */
068            private Object output;
069    
070            /** Absolute pathname of file on web server. */
071            private Resource file;
072    
073            /** Applies only to Solaris and HP-UX. Permissions. Octal values of UNIX chmod command. Assigned to owner, group, and other, respectively. */
074            private int mode=-1;
075    
076            /** Name of variable to contain contents of text file. */
077            private String variable;
078    
079            /** Name of form field used to select the file. */
080            private String filefield;
081    
082            /** Character set name for the file contents. */
083            private String charset=null;
084    
085            /** Yes: appends newline character to text written to file */
086            private boolean addnewline=true;
087            private boolean fixnewline=true;
088            /** One attribute (Windows) or a comma-delimited list of attributes (other platforms) to set on the file. 
089            ** If omitted, the file's attributes are maintained. */
090            private String attributes;
091    
092            /** Absolute pathname of file on web server. 
093            ** On Windows, use backward slashes; on UNIX, use forward slashes. */
094            private Resource source;
095    
096            /** Action to take if filename is the same as that of a file in the directory. */
097            private int nameconflict=NAMECONFLICT_UNDEFINED;
098    
099            /** Limits the MIME types to accept. Comma-delimited list. For example, to permit JPG and Microsoft Word file uploads:
100            ** accept = "image/jpg, application/msword"
101            ** The browser uses file extension to determine file type. */
102            private String accept;
103        
104        private String result=null;
105            
106            private railo.runtime.security.SecurityManager securityManager;
107    
108            private String serverPassword=null;
109            private Object acl=null;
110    
111            /**
112            * @see javax.servlet.jsp.tagext.Tag#release()
113            */
114            public void release()   {
115                    super.release();
116                    acl=null;
117                    action=null;
118                    strDestination=null;
119                    output=null;
120                    file=null;
121                    mode=-1;
122                    variable=null;
123                    filefield=null;
124                    charset=null;
125                    addnewline=true;
126                    fixnewline=true;
127                    attributes=null;
128                    source=null;
129                    nameconflict=NAMECONFLICT_UNDEFINED;
130                    accept=null;
131                    securityManager=null;
132            result=null;
133            serverPassword=null;
134            }
135    
136            /** set the value action
137            *  Type of file manipulation that the tag performs.
138            * @param action value to set
139            **/     
140            public void setAction(String action)    {
141                    this.action=action.toLowerCase();
142            }
143    
144            /** set the value destination
145            *  Absolute pathname of directory or file on web server.
146            * @param destination value to set
147            **/
148            public void setDestination(String destination)  {
149                    this.strDestination=destination;//ResourceUtil.toResourceNotExisting(pageContext ,destination);
150            }
151    
152            /** set the value output
153            *  Content of the file to be created.
154            * @param output value to set
155            **/
156            public void setOutput(Object output)    {
157                    if(output==null)this.output="";
158                    else this.output=output;
159            }
160    
161            /** set the value file
162            *  Absolute pathname of file on web server.
163            * @param file value to set
164            **/
165            public void setFile(String file)        {
166                    this.file=ResourceUtil.toResourceNotExisting(pageContext ,file);
167            
168            }
169    
170            /** set the value mode
171            *  Applies only to Solaris and HP-UX. Permissions. Octal values of UNIX chmod command. Assigned to owner, group, and other, respectively.
172            * @param mode value to set
173             * @throws PageException 
174            **/
175            public void setMode(String mode) throws PageException   {
176                    this.mode=toMode(mode);
177            }
178            
179    
180            public static int toMode(String mode) throws PageException      {
181                    if(StringUtil.isEmpty(mode,true)) return -1;
182                    try {
183                            return ModeUtil.toOctalMode(mode);
184                    } 
185                    catch (IOException e) {
186                            throw Caster.toPageException(e);
187                    }
188            }
189            
190    
191            /** set the value variable
192            *  Name of variable to contain contents of text file.
193            * @param variable value to set
194            **/
195            public void setVariable(String variable)        {
196                    this.variable=variable;
197            }
198    
199            /** set the value filefield
200            *  Name of form field used to select the file.
201            * @param filefield value to set
202            **/
203            public void setFilefield(String filefield)      {
204                    this.filefield=filefield;
205            }
206    
207            /** set the value charset
208            *  Character set name for the file contents.
209            * @param charset value to set
210            **/
211            public void setCharset(String charset)  {
212                    this.charset=charset.trim();
213            }
214            
215            /** set the value acl
216            *  used only for s3 resources, for all others ignored
217            * @param charset value to set
218             * @throws ApplicationException 
219             * @Deprecated only exists for backward compatibility to old ra files.
220            **/
221            public void setAcl(String acl) throws ApplicationException      {
222                    this.acl=acl;
223            }
224            public void setAcl(Object acl) throws ApplicationException      {
225                    this.acl=acl;
226            }
227            public void setStoreacl(Object acl) throws ApplicationException {
228                    this.acl=acl;
229            }
230            
231            
232            
233            public void setServerpassword(String serverPassword)    {
234                this.serverPassword=serverPassword;
235            }
236    
237            /** set the value addnewline
238            *  Yes: appends newline character to text written to file
239            * @param addnewline value to set
240            **/
241            public void setAddnewline(boolean addnewline)   {
242                    this.addnewline=addnewline;
243            }
244    
245            /** set the value attributes
246            *  One attribute (Windows) or a comma-delimited list of attributes (other platforms) to set on the file. 
247            * If omitted, the file's attributes are maintained.
248            * @param attributes value to set
249            **/
250            public void setAttributes(String attributes)    {
251                    this.attributes=attributes;
252            }
253    
254            /** set the value source
255            *  Absolute pathname of file on web server. 
256            * On Windows, use backward slashes; on UNIX, use forward slashes.
257            * @param source value to set
258            **/
259            public void setSource(String source)    {
260                    this.source=ResourceUtil.toResourceNotExisting(pageContext ,source);
261            }
262    
263            /** set the value nameconflict
264            *  Action to take if filename is the same as that of a file in the directory.
265            * @param nameconflict value to set
266             * @throws ApplicationException 
267            **/
268            public void setNameconflict(String nameconflict) throws ApplicationException    {
269                    this.nameconflict=toNameconflict(nameconflict);
270            }
271            
272            public static int toNameconflict(String nameconflict) throws ApplicationException       {
273                    if(StringUtil.isEmpty(nameconflict,true)) return NAMECONFLICT_UNDEFINED;
274                    
275                    nameconflict=nameconflict.toLowerCase().trim();
276                    if("error".equals(nameconflict))                        return NAMECONFLICT_ERROR;
277                    else if("skip".equals(nameconflict))            return NAMECONFLICT_SKIP;
278                    else if("overwrite".equals(nameconflict))       return NAMECONFLICT_OVERWRITE;
279                    else if("makeunique".equals(nameconflict))      return NAMECONFLICT_MAKEUNIQUE;
280                    else throw new ApplicationException("invalid value for attribute/argument nameconflict ["+nameconflict+"]",
281                                    "valid values are [error,skip,overwrite,makeunique]");
282            }
283            
284            
285    
286            /** set the value accept
287            *  Limits the MIME types to accept. Comma-delimited list. For example, to permit JPG and Microsoft Word file uploads:
288            * accept = "image/jpg, application/msword"
289            * The browser uses file extension to determine file type.
290            * @param accept value to set
291            **/
292            public void setAccept(String accept)    {
293                    this.accept=accept;
294            }
295        
296        /**
297         * @param result The result to set.
298         */
299        public void setResult(String result) {
300            this.result = result;
301        }
302    
303    
304            /**
305            * @see javax.servlet.jsp.tagext.Tag#doStartTag()
306            */
307            public int doStartTag() throws PageException    {
308                    
309                    if(StringUtil.isEmpty(charset)) charset=pageContext.getConfig().getResourceCharset();
310                    
311                securityManager = pageContext.getConfig().getSecurityManager();
312                    if(action.equals("move")) actionMove(pageContext, securityManager,source, strDestination, nameconflict,serverPassword,acl, mode, attributes);
313                    else if(action.equals("rename")) actionMove(pageContext, securityManager,source, strDestination, nameconflict,serverPassword,acl, mode, attributes);
314                    else if(action.equals("copy")) actionCopy(pageContext, securityManager,source, strDestination, nameconflict,serverPassword,acl, mode, attributes);
315                    else if(action.equals("delete")) actionDelete();
316                    else if(action.equals("read")) actionRead();
317                    else if(action.equals("readbinary")) actionReadBinary();
318                    else if(action.equals("write")) actionWrite();
319                    else if(action.equals("append")) actionAppend();
320                    else if(action.equals("upload")) actionUpload();
321                    else if(action.equals("uploadall")) actionUploadAll();
322            else if(action.equals("info")) actionInfo();
323            else if(action.equals("touch")) actionTouch();
324            else 
325                            throw new ApplicationException("invalid value ["+action+"] for attribute action","values for attribute action are:info,move,rename,copy,delete,read,readbinary,write,append,upload,uploadall");
326                                    
327                    return SKIP_BODY;
328            }
329    
330            /**
331            * @see javax.servlet.jsp.tagext.Tag#doEndTag()
332            */
333            public int doEndTag()   {
334                    return EVAL_PAGE;
335            }
336    
337            /**
338             * move source file to destination path or file
339             * @throws PageException
340             */
341            public static void actionMove(PageContext pageContext, railo.runtime.security.SecurityManager securityManager,
342                            Resource source, String strDestination, int nameconflict,String serverPassword,
343                            Object acl, int mode, String attributes) throws PageException {
344                    if(nameconflict==NAMECONFLICT_UNDEFINED) nameconflict=NAMECONFLICT_OVERWRITE;
345                    
346                    if(source==null)
347                            throw new ApplicationException("attribute source is not defined for tag file");
348                    if(StringUtil.isEmpty(strDestination))
349                            throw new ApplicationException("attribute destination is not defined for tag file");
350                    
351                    Resource destination=toDestination(pageContext,strDestination,source);
352                    
353                    securityManager.checkFileLocation(pageContext.getConfig(),source,serverPassword);
354                    securityManager.checkFileLocation(pageContext.getConfig(),destination,serverPassword);
355                    if(source.equals(destination)) return ;
356                    
357                    // source
358                    if(!source.exists())
359                            throw new ApplicationException("source file ["+source.toString()+"] doesn't exist");
360                    else if(!source.isFile())
361                            throw new ApplicationException("source file ["+source.toString()+"] is not a file");
362                    else if(!source.isReadable() || !source.isWriteable())
363                            throw new ApplicationException("no access to source file ["+source.toString()+"]");
364                    
365                    // destination
366                    if(destination.isDirectory()) destination=destination.getRealResource(source.getName());
367                    if(destination.exists()) {
368                            // SKIP
369                            if(nameconflict==NAMECONFLICT_SKIP) return;
370                            // OVERWRITE
371                            else if(nameconflict==NAMECONFLICT_OVERWRITE) destination.delete();
372                            // MAKEUNIQUE
373                            else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) destination=makeUnique(destination);
374                            // ERROR
375                            else throw new ApplicationException("destiniation file ["+destination.toString()+"] already exist");
376                    }
377                            
378            
379                    try {
380                            source.moveTo(destination);
381                                    
382                    }
383                    catch(Throwable t) {t.printStackTrace();
384                            throw new ApplicationException(t.getMessage());
385                    }
386                    setACL(destination,acl);
387                    setMode(destination,mode);
388            setAttributes(destination,attributes);
389            }
390    
391            private static Resource toDestination(PageContext pageContext,String path, Resource source) {
392                    if(source!=null && path.indexOf(File.separatorChar)==-1 && path.indexOf('/')==-1 && path.indexOf('\\')==-1) {
393                            Resource p = source.getParentResource();
394                            if(p!=null)return p.getRealResource(path);
395                    }
396                    return ResourceUtil.toResourceNotExisting(pageContext ,path);
397            }
398    
399            /**
400             * copy source file to destination file or path
401             * @throws PageException
402             */
403            public static void actionCopy(PageContext pageContext, railo.runtime.security.SecurityManager securityManager,
404                            Resource source, String strDestination, int nameconflict,String serverPassword,
405                            Object acl, int mode, String attributes) throws PageException {
406                    if(nameconflict==NAMECONFLICT_UNDEFINED) nameconflict=NAMECONFLICT_OVERWRITE;
407                    
408                    if(source==null)
409                            throw new ApplicationException("attribute source is not defined for tag file");
410                    if(StringUtil.isEmpty(strDestination))
411                            throw new ApplicationException("attribute destination is not defined for tag file");
412    
413                    Resource destination=toDestination(pageContext,strDestination,source);
414                    
415                    
416                    securityManager.checkFileLocation(pageContext.getConfig(),source,serverPassword);
417                    securityManager.checkFileLocation(pageContext.getConfig(),destination,serverPassword);
418                    
419                    // source
420                    if(!source.exists())
421                            throw new ApplicationException("source file ["+source.toString()+"] doesn't exist");
422                    else if(!source.isFile())
423                            throw new ApplicationException("source file ["+source.toString()+"] is not a file");
424                    else if(!source.canRead())
425                            throw new ApplicationException("no access to source file ["+source.toString()+"]");
426                    
427                    // destination
428                    if(destination.isDirectory()) destination=destination.getRealResource(source.getName());
429                    if(destination.exists()) {
430                            // SKIP
431                            if(nameconflict==NAMECONFLICT_SKIP) return;
432                            // SKIP
433                            else if(nameconflict==NAMECONFLICT_OVERWRITE) destination.delete();
434                            // MAKEUNIQUE
435                            else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) destination=makeUnique(destination);
436                            // ERROR
437                            else throw new ApplicationException("destiniation file ["+destination.toString()+"] already exist");
438                    }
439                    
440            try {
441                IOUtil.copy(source,destination);                    
442                    }
443                    catch(IOException e) {
444                            
445                ApplicationException ae = new ApplicationException("can't copy file ["+source+"] to ["+destination+"]",e.getMessage());
446                ae.setStackTrace(e.getStackTrace());
447                throw ae;
448                    }
449                    setACL(destination,acl);
450                    setMode(destination,mode);
451            setAttributes(destination,attributes);
452            }
453    
454            private static void setACL(Resource res,Object acl) throws PageException {
455                    String scheme = res.getResourceProvider().getScheme();
456                    
457                    if("s3".equalsIgnoreCase(scheme)){
458                            S3Resource s3r=(S3Resource) res;
459                            
460                            if(acl!=null){
461                                    try {
462                                            // old way
463                                            if(Decision.isString(acl)) {
464                                                    s3r.setACL(S3.toIntACL(Caster.toString(acl)));
465                                            }
466                                            // new way
467                                            else {
468                                                    StoreSetACL.invoke(s3r, acl);
469                                            }
470                                    } catch (IOException e) {
471                                            throw Caster.toPageException(e);
472                                    }
473                            }
474                            
475                    }
476                    // set acl for s3 resource
477                    /*if(res instanceof S3Resource) {
478                            ((S3Resource)res).setACL(acl);
479                    }*/
480            }
481    
482            private static Resource makeUnique(Resource res) {
483    
484                    String ext=getFileExtension(res);
485                    String name=getFileName(res);
486                    ext=(ext==null)?"":"."+ext;
487                    int count=0;
488                    while(res.exists()) {
489                            res=res.getParentResource().getRealResource(name+(++count)+ext);
490                    }
491                    
492                    return res;
493            }
494    
495            /**
496             * copy source file to destination file or path
497             * @throws PageException 
498             */
499            private void actionDelete() throws PageException {
500                    checkFile(false,false,false);
501                    setACL(file,acl);
502                    try {
503                            if(!file.delete()) throw new ApplicationException("can't delete file ["+file+"]");
504                    }
505                    catch(Throwable t) {
506                            throw new ApplicationException(t.getMessage());
507                    }
508            }
509    
510            /**
511             * read source file
512             * @throws PageException
513             */
514            private void actionRead() throws PageException {
515                    if(variable==null)
516                            throw new ApplicationException("attribute variable is not defined for tag file");
517                    checkFile(false,true,false);
518                    //print.ln(charset);
519                    //TextFile tf=new TextFile(file.getAbsolutePath());
520                            
521                    try {
522                        pageContext.setVariable(variable,IOUtil.toString(file,charset));
523                    }
524            catch (IOException e) {
525                    
526                            throw new ApplicationException("can't read file ["+file+"]",e.getMessage());
527                    }
528    
529            }
530    
531            /**
532             * read source file
533             * @throws PageException
534             */
535            private void actionReadBinary() throws PageException {
536                    if(variable==null)
537                            throw new ApplicationException("attribute variable is not defined for tag file");
538                    checkFile(false,true,false);
539                    
540                    //TextFile tf=new TextFile(file.getAbsolutePath());
541                    
542                    try {
543                pageContext.setVariable(variable,IOUtil.toBytes(file));
544                    }catch (IOException e) {
545                            throw new ApplicationException("can't read binary file ["+source.toString()+"]",e.getMessage());
546                    }
547            }
548            
549        /**
550         * write to the source file
551         * @throws PageException
552         */
553        private void actionWrite() throws PageException {
554            if(output==null)
555                throw new ApplicationException("attribute output is not defined for tag file");
556            checkFile(true,false,true);
557            
558            try {
559                    if(output instanceof InputStream)       {
560                            IOUtil.copy(
561                                            (InputStream)output,
562                                            file,
563                                            false);
564                    }
565                    else if(Decision.isCastableToBinary(output,false))      {
566                            IOUtil.copy(
567                                            new ByteArrayInputStream(Caster.toBinary(output)), 
568                                            file,
569                                            true);
570                    }
571                    else {
572                            String content=Caster.toString(output);
573                            if(fixnewline)content=doFixNewLine(content);
574                            if(addnewline) content+=SystemUtil.getOSSpecificLineSeparator();
575                            
576                    if(content.length()==0)ResourceUtil.touch(file);
577                    else IOUtil.write(file,content,charset,false);
578                            
579                    }    
580            } 
581            catch (UnsupportedEncodingException e) {
582                throw new ApplicationException("Unsupported Charset Definition ["+charset+"]",e.getMessage());
583            }
584            catch (IOException e) {
585                
586                throw new ApplicationException("can't write file "+file.getAbsolutePath(),e.getMessage());
587            }
588            setACL(file,acl);
589            setMode(file,mode);
590            setAttributes(file,attributes);
591        }
592        
593        /**
594         * write to the source file
595         * @throws PageException
596         */
597        private void actionTouch() throws PageException {
598            checkFile(true,true,true);
599            
600            try {
601                ResourceUtil.touch(file);
602            } 
603            catch (IOException e) {
604                
605                throw new ApplicationException("can't touch file "+file.getAbsolutePath(),e.getMessage());
606            }
607            
608            setACL(file,acl);
609            setMode(file,mode);
610            setAttributes(file,attributes);
611        }
612        
613        
614    
615            /**
616             * append data to source file
617             * @throws PageException
618             */
619            private void actionAppend() throws PageException {
620                    if(output==null)
621                            throw new ApplicationException("attribute output is not defined for tag file");
622                    checkFile(true,false,true);
623                    
624            try {
625    
626                if(!file.exists()) file.createNewFile();
627                String content=Caster.toString(output);
628                if(fixnewline)content=doFixNewLine(content);
629                    if(addnewline) content+=SystemUtil.getOSSpecificLineSeparator();
630                IOUtil.write(file,content,charset,true);
631                    
632            } 
633                    catch (UnsupportedEncodingException e) {
634                throw new ApplicationException("Unsupported Charset Definition ["+charset+"]",e.getMessage());
635            }
636            catch (IOException e) {
637                throw new ApplicationException("can't write file",e.getMessage());
638            }
639            setACL(file,acl);
640                    setMode(file,mode);
641            setAttributes(file,attributes);
642            }
643    
644        private String doFixNewLine(String content) {
645                    // TODO replace new line with system new line
646                    return content;
647            }
648    
649            /**
650             * list all files and directories inside a directory
651             * @throws PageException
652             */
653            private void actionInfo() throws PageException {
654                    
655                    if(variable==null)
656                            throw new ApplicationException("attribute variable is not defined for tag file");
657                    checkFile(false,false,false);
658                    
659                    Struct sct =new StructImpl();
660                    pageContext.setVariable(variable,sct);
661                    
662                    // fill data to query
663                    sct.setEL(KeyImpl.NAME,file.getName());
664                    sct.setEL(KeyImpl.SIZE,Long.valueOf(file.length()));
665                    sct.setEL(KeyImpl.TYPE,file.isDirectory()?"Dir":"File");
666                    sct.setEL("dateLastModified",new DateTimeImpl(pageContext,file.lastModified(),false));
667                    sct.setEL("attributes",getFileAttribute(file));
668                    if(SystemUtil.isUnix())sct.setEL("mode",new ModeObjectWrap(file));
669            
670                    try {           
671                            BufferedImage bi = ImageUtil.toBufferedImage(file, null);
672                if(bi!=null) {
673                        Struct img =new StructImpl();
674                        img.setEL("width",new Double(bi.getWidth()));
675                        img.setEL("height",new Double(bi.getHeight()));
676                        sct.setEL("img",img);
677                }
678            } 
679                    catch (Throwable t) {}
680            }
681    
682            private static String getFileAttribute(Resource file){
683                    return  file.exists() && !file.canWrite() ? "R".concat(file.isHidden() ? "H" : "") : file.isHidden() ? "H" : "";
684            }
685            
686            /**
687             * read source file
688             * @throws PageException
689             */
690    
691            public void actionUpload() throws PageException {
692                    FormImpl.Item item=getFormItem(pageContext,filefield);
693                    Struct cffile = _actionUpload(pageContext,securityManager,item,strDestination,nameconflict,accept,mode,attributes,acl,serverPassword);
694                    if(StringUtil.isEmpty(result)) {
695                pageContext.undefinedScope().set("file",cffile);
696                        pageContext.undefinedScope().set("cffile",cffile);
697            }
698            else {
699                pageContext.setVariable(result,cffile);
700            }
701            }
702            
703            
704            public static Struct actionUpload(PageContext pageContext,railo.runtime.security.SecurityManager securityManager,String filefield,
705                            String strDestination,int nameconflict,String accept,int mode,String attributes,Object acl,String serverPassword) throws PageException {
706                    FormImpl.Item item=getFormItem(pageContext,filefield);
707                    return _actionUpload(pageContext,securityManager,item,strDestination,nameconflict,accept,mode,attributes,acl,serverPassword);
708            }
709            
710            public void actionUploadAll() throws PageException {
711                    Array arr=actionUploadAll(pageContext,securityManager,strDestination,nameconflict,accept,mode,attributes,acl,serverPassword);
712                    if(StringUtil.isEmpty(result)) {
713                            Struct sct;
714                            if(arr!=null && arr.size()>0) sct=(Struct) arr.getE(1);
715                            else sct=new StructImpl();
716                            
717                pageContext.undefinedScope().set("file",sct);
718                        pageContext.undefinedScope().set("cffile",sct);
719            }
720            else {
721                pageContext.setVariable(result,arr);
722            }
723            }
724            
725    
726            public static Array actionUploadAll(PageContext pageContext,railo.runtime.security.SecurityManager securityManager,
727                            String strDestination,int nameconflict,String accept,int mode,String attributes,Object acl,String serverPassword) throws PageException {
728                    Item[] items=getFormItems(pageContext);
729                    Struct sct=null;
730                    Array arr=new ArrayImpl();
731                    for(int i=0;i<items.length;i++){
732                            sct = _actionUpload(pageContext,securityManager,items[i],strDestination,nameconflict,accept,mode,attributes,acl,serverPassword);
733                            arr.appendEL(sct);
734                    }
735                    return arr;
736            }
737            
738            private static synchronized Struct _actionUpload(PageContext pageContext, railo.runtime.security.SecurityManager securityManager, 
739                            Item formItem,String strDestination,int nameconflict,String accept,int mode,String attributes,Object acl,String serverPassword) throws PageException {
740                    if(nameconflict==NAMECONFLICT_UNDEFINED) nameconflict=NAMECONFLICT_ERROR;
741    
742                    boolean fileWasRenamed=false;
743                    boolean fileWasAppended=false;
744                    boolean fileExisted=false;
745                    boolean fileWasOverwritten=false;
746                    
747                    
748                    String contentType=formItem.getContentType();
749                    
750                    // check file type
751                    checkContentType(pageContext,contentType,accept);
752                    
753                    // set cffile struct
754                    Struct cffile=new StructImpl();
755            
756            long length = formItem.getResource().length();
757                    cffile.set("timecreated",new DateTimeImpl(pageContext.getConfig()));
758                    cffile.set("timelastmodified",new DateTimeImpl(pageContext.getConfig()));
759                    cffile.set("datelastaccessed",new DateImpl(pageContext));
760                    cffile.set("oldfilesize",Long.valueOf(length));
761                    cffile.set("filesize",Long.valueOf(length));
762                    cffile.set("contenttype",ListFirst.call(pageContext,contentType,"/"));
763                    cffile.set("contentsubtype",ListLast.call(pageContext,contentType,"/"));
764                    
765                    // client file
766                    String strClientFile=formItem.getName();
767                    while(strClientFile.indexOf('\\')!=-1)
768                            strClientFile=strClientFile.replace('\\','/');
769                    Resource clientFile=pageContext.getConfig().getResource(strClientFile);
770                    String clientFileName=clientFile.getName();
771                            
772                            //String dir=clientFile.getParent();
773                            //dir=correctDirectory(dir);
774                    
775                            cffile.set("clientdirectory",getParent(clientFile));
776                            cffile.set("clientfile",clientFile.getName());
777                            cffile.set("clientfileext",getFileExtension(clientFile));
778                            cffile.set("clientfilename",getFileName(clientFile));
779                    
780                // check destination
781                if(StringUtil.isEmpty(strDestination))
782                    throw new ApplicationException("attribute destination is not defined in tag file");
783    
784                
785                Resource destination=toDestination(pageContext,strDestination,null);
786                
787                
788                    securityManager.checkFileLocation(pageContext.getConfig(),destination,serverPassword);
789                    
790               // destination.getCanonicalPath()
791                if(destination.isDirectory()) 
792                    destination=destination.getRealResource(clientFileName);
793                else if(!clientFileName.equalsIgnoreCase(destination.getName()))
794                    fileWasRenamed=true;
795                
796                // check parent destination -> directory of the desinatrion
797                Resource parentDestination=destination.getParentResource();
798                
799                if(!parentDestination.exists())
800                    throw new ApplicationException("attribute destination has a invalid value ["+destination+"], directory ["+parentDestination+"] doesn't exist");
801                else if(!parentDestination.canWrite())
802                    throw new ApplicationException("can't write to destination directory ["+parentDestination+"], no access to write");
803                
804                // set server variables
805                    cffile.set("serverdirectory",getParent(destination));
806                    cffile.set("serverfile",destination.getName());
807                    cffile.set("serverfileext",getFileExtension(destination));
808                    cffile.set("serverfilename",getFileName(destination));
809                    cffile.set("attemptedserverfile",destination.getName());
810                
811                    
812                // check nameconflict
813                if(destination.exists()) {
814                    fileExisted=true;
815                    if(nameconflict==NAMECONFLICT_ERROR) {
816                            throw new ApplicationException("destination file ["+destination+"] already exist");
817                    }
818                    else if(nameconflict==NAMECONFLICT_SKIP) {
819                                    cffile.set("fileexisted",Caster.toBoolean(fileExisted));
820                                    cffile.set("filewasappended",Boolean.FALSE);
821                                    cffile.set("filewasoverwritten",Boolean.FALSE);
822                                    cffile.set("filewasrenamed",Boolean.FALSE);
823                                    cffile.set("filewassaved",Boolean.FALSE);
824                            return cffile;
825                    }
826                    else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) {
827                            destination=makeUnique(destination);
828                            fileWasRenamed=true;
829                            
830                                    //if(fileWasRenamed) {
831                                    cffile.set("serverdirectory",getParent(destination));
832                                    cffile.set("serverfile",destination.getName());
833                                    cffile.set("serverfileext",getFileExtension(destination));
834                                    cffile.set("serverfilename",getFileName(destination));
835                                    cffile.set("attemptedserverfile",destination.getName());        
836                                    //}
837                    }
838                    else if(nameconflict==NAMECONFLICT_OVERWRITE) {
839                            //fileWasAppended=true; 
840                            fileWasOverwritten=true;
841                            if(!destination.delete())
842                                    if(destination.exists()) // hier hatte ich concurrent problem das damit ausgeraeumt ist
843                                            throw new ApplicationException("can't delete destination file ["+destination+"]");
844                    }
845                    // for "overwrite" no action is neded
846                    
847                }
848                
849                            try {
850                                    destination.createNewFile();
851                                    IOUtil.copy(formItem.getResource(),destination);
852                            }
853                            catch(Throwable t) {
854                                    throw new ApplicationException(t.getMessage());
855                            }
856                            
857                            // Set cffile/file struct
858                            
859                            cffile.set("fileexisted",Caster.toBoolean(fileExisted));
860                            cffile.set("filewasappended",Caster.toBoolean(fileWasAppended));
861                            cffile.set("filewasoverwritten",Caster.toBoolean(fileWasOverwritten));
862                            cffile.set("filewasrenamed",Caster.toBoolean(fileWasRenamed));
863                            cffile.set("filewassaved",Boolean.TRUE);
864                            
865    
866                            setACL(destination,acl);
867                            setMode(destination,mode);
868                    setAttributes(destination, attributes);
869                    return cffile;
870            }
871    
872            /**
873             * check if the content ii ok
874             * @param contentType 
875             * @throws PageException
876             */
877            private static void checkContentType(PageContext pageContext,String contentType,String accept) throws PageException {
878                    String type=ListFirst.call(pageContext,contentType,"/").trim().toLowerCase();
879                    String subType=ListLast.call(pageContext,contentType,"/").trim().toLowerCase();
880                    
881                    if(accept==null || accept.trim().length()==0) return;
882                    
883                    Array whishedTypes=List.listToArrayRemoveEmpty(accept,',');
884                    int len=whishedTypes.size();
885                    for(int i=1;i<=len;i++) {
886                            String whishedType=Caster.toString(whishedTypes.getE(i)).trim();
887                            String wType=ListFirst.call(pageContext,whishedType,"/").trim().toLowerCase();
888                            String wSubType=ListLast.call(pageContext,whishedType,"/").trim().toLowerCase();
889                            if((wType.equals("*") || wType.equals(type)) && (wSubType.equals("*") || wSubType.equals(subType)))return;
890                            
891                    }
892                    throw new ApplicationException("The MIME type of the uploaded file ["+contentType+"] was not accepted by the server.","only this ["+accept+"] mime type are accepted");
893            }
894    
895            /**
896             * rreturn fileItem matching to filefiled definition or throw a exception
897             * @return FileItem
898             * @throws ApplicationException
899             */
900            private static Item getFormItem(PageContext pageContext, String filefield) throws PageException {
901                    // check filefield
902                    if(StringUtil.isEmpty(filefield)){
903                            Item[] items = getFormItems(pageContext);
904                            if(ArrayUtil.isEmpty(items))
905                                    throw new ApplicationException("no file send with this form");
906                            return items[0];
907                    }
908                            
909                    PageException pe = pageContext.formScope().getInitException();
910                    if(pe!=null) throw pe;
911                    FormUpload upload = (FormUpload)pageContext.formScope();
912                    FormImpl.Item fileItem = upload.getUploadResource(filefield);
913                    if(fileItem==null) {
914                            Item[] items = upload.getFileItems();
915                            StringBuilder sb=new StringBuilder();
916                            for(int i=0;i<items.length;i++){
917                                    if(i!=0) sb.append(", ");
918                                    sb.append(items[i].getFieldName());
919                            }
920                            String add=".";
921                            if(sb.length()>0) add=", valid field names are ["+sb+"].";
922                            
923                            
924                            if(pageContext.formScope().get(filefield,null)==null) 
925                                    throw new ApplicationException("form field ["+filefield+"] is not a file field"+add);
926                            throw new ApplicationException("form field ["+filefield+"] doesn't exist or has no content"+add);
927                    }
928                    
929                    return fileItem;
930            }
931            
932            private static Item[] getFormItems(PageContext pageContext) throws PageException {
933                    PageException pe = pageContext.formScope().getInitException();
934                    if(pe!=null) throw pe;
935                    
936                    FormUpload scope = (FormUpload) pageContext.formScope();
937                    return scope.getFileItems();
938            }
939            
940            
941            /**
942             * get file extension of a file object
943             * @param file file object
944             * @return extnesion
945             */
946            private static String getFileExtension(Resource file) {
947                    String name=file.getName();
948                    String[] arr;
949                    try {
950                            arr = List.toStringArray(List.listToArrayRemoveEmpty(name, '.'));
951                    } catch (PageException e) {
952                            arr=null;
953                    }
954                    if(arr.length<2) return "";
955                    
956                    return arr[arr.length-1];
957            }
958            
959            /**
960             * get file name of a file object without extension
961             * @param file file object
962             * @return name of the file 
963             */
964            private static String getFileName(Resource file) {
965                    String name=file.getName();
966                    int pos=name.lastIndexOf(".");
967                    
968                    if(pos==-1)return name;
969                    return name.substring(0,pos);
970            }
971            
972            /*private String correctDirectory(Resource resource) {
973                    if(StringUtil.isEmpty(resource,true)) return "";
974                    resource=resource.trim();
975                    if((StringUtil.endsWith(resource, '/') || StringUtil.endsWith(resource, '\\')) && resource.length()>1) {
976                            return resource.substring(0,resource.length()-1);
977                    }
978                    return resource;
979            }*/
980            
981            private static String getParent(Resource res) {
982                    Resource parent = res.getParentResource();
983                    //print.out("res:"+res);
984                    //print.out("parent:"+parent);
985                    if(parent==null) return "";
986                    return ResourceUtil.getCanonicalPathEL(parent);
987            }
988            
989    
990            private void checkFile(boolean create, boolean canRead, boolean canWrite) throws PageException {
991                    if(file==null)
992                            throw new ApplicationException("attribute file is not defined for tag file");
993    
994                    securityManager.checkFileLocation(pageContext.getConfig(),file,serverPassword);
995                    if(!file.exists()) {
996                            if(create) {
997                                    Resource parent=file.getParentResource();
998                                    if(parent!=null && !parent.exists())
999                                            throw new ApplicationException("parent directory for ["+file+"] doesn't exists");
1000                                    try {
1001                                            file.createFile(false);
1002                                    } catch (IOException e) {
1003                                            throw new ApplicationException("invalid file ["+file+"]",e.getMessage());
1004                                    }
1005                            }
1006                            else if(!file.isFile()) 
1007                                    throw new ApplicationException("source file ["+file.toString()+"] is not a file");
1008                            else 
1009                                    throw new ApplicationException("source file ["+file.toString()+"] doesn't exist");
1010                    }
1011                    else if(!file.isFile())
1012                            throw new ApplicationException("source file ["+file.toString()+"] is not a file");
1013            else if(canRead &&!file.canRead())
1014                throw new ApplicationException("no read access to source file ["+file.toString()+"]");
1015            else if(canWrite && !file.canWrite())
1016                throw new ApplicationException("no write access to source file ["+file.toString()+"]");
1017            
1018            }
1019    
1020            /**
1021             * set attributes on file
1022         * @param file
1023             * @throws PageException
1024         */
1025        private static void setAttributes(Resource file,String attributes) throws PageException {
1026            if(!SystemUtil.isWindows() || StringUtil.isEmpty(attributes)) return;
1027            try {
1028                    ResourceUtil.setAttribute(file, attributes);
1029            } 
1030            catch (IOException e) {
1031                throw new ApplicationException("can't change attributes of file "+file,e.getMessage());
1032            }
1033        }
1034    
1035        /**
1036             * change mode of given file
1037         * @param file
1038             * @throws ApplicationException
1039         */
1040        private static void setMode(Resource file,int mode) throws ApplicationException {
1041            if(mode==-1 || SystemUtil.isWindows()) return;
1042            try {
1043                    file.setMode(mode);
1044                //FileUtil.setMode(file,mode);
1045            } catch (IOException e) {
1046                throw new ApplicationException("can't change mode of file "+file,e.getMessage());
1047            }
1048        }
1049    
1050            /**
1051             * @param fixnewline the fixnewline to set
1052             */
1053            public void setFixnewline(boolean fixnewline) {
1054                    this.fixnewline = fixnewline;
1055            }
1056    }