001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.tag;
020
021import static lucee.runtime.tag.util.FileUtil.NAMECONFLICT_ERROR;
022import static lucee.runtime.tag.util.FileUtil.NAMECONFLICT_OVERWRITE;
023import static lucee.runtime.tag.util.FileUtil.NAMECONFLICT_SKIP;
024import static lucee.runtime.tag.util.FileUtil.NAMECONFLICT_UNDEFINED;
025
026import java.io.File;
027import java.io.IOException;
028import java.util.Date;
029
030import lucee.commons.io.ModeUtil;
031import lucee.commons.io.res.Resource;
032import lucee.commons.io.res.ResourceMetaData;
033import lucee.commons.io.res.filter.AndResourceFilter;
034import lucee.commons.io.res.filter.DirectoryResourceFilter;
035import lucee.commons.io.res.filter.FileResourceFilter;
036import lucee.commons.io.res.filter.NotResourceFilter;
037import lucee.commons.io.res.filter.OrResourceFilter;
038import lucee.commons.io.res.filter.ResourceFilter;
039import lucee.commons.io.res.filter.ResourceNameFilter;
040import lucee.commons.io.res.type.file.FileResource;
041import lucee.commons.io.res.type.s3.S3;
042import lucee.commons.io.res.type.s3.S3Constants;
043import lucee.commons.io.res.type.s3.S3Exception;
044import lucee.commons.io.res.type.s3.S3Resource;
045import lucee.commons.io.res.util.ModeObjectWrap;
046import lucee.commons.io.res.util.ResourceAndResourceNameFilter;
047import lucee.commons.io.res.util.ResourceUtil;
048import lucee.commons.io.res.util.UDFFilter;
049import lucee.commons.io.res.util.WildcardPatternFilter;
050import lucee.commons.lang.ExceptionUtil;
051import lucee.commons.lang.StringUtil;
052import lucee.runtime.PageContext;
053import lucee.runtime.exp.ApplicationException;
054import lucee.runtime.exp.PageException;
055import lucee.runtime.ext.tag.TagImpl;
056import lucee.runtime.functions.s3.StoreSetACL;
057import lucee.runtime.op.Caster;
058import lucee.runtime.op.Decision;
059import lucee.runtime.security.SecurityManager;
060import lucee.runtime.tag.util.FileUtil;
061import lucee.runtime.type.Array;
062import lucee.runtime.type.ArrayImpl;
063import lucee.runtime.type.Collection.Key;
064import lucee.runtime.type.Query;
065import lucee.runtime.type.QueryImpl;
066import lucee.runtime.type.UDF;
067import lucee.runtime.type.util.KeyConstants;
068
069/**
070* Handles interactions with directories.
071*
072*
073*
074**/
075public final class Directory extends TagImpl  {
076
077        public static final int TYPE_ALL = 0;
078        public static final int TYPE_FILE = 1;
079        public static final int TYPE_DIR = 2;
080        
081        public static final ResourceFilter DIRECTORY_FILTER = new DirectoryResourceFilter();
082        public static final ResourceFilter FILE_FILTER = new FileResourceFilter();
083        
084        private static final Key MODE = KeyConstants._mode;
085        private static final Key META = KeyConstants._meta;
086        private static final Key DATE_LAST_MODIFIED = KeyConstants._dateLastModified;
087        private static final Key ATTRIBUTES = KeyConstants._attributes;
088        private static final Key DIRECTORY = KeyConstants._directory;
089                
090        public static final int LIST_INFO_QUERY_ALL = 1;
091        public static final int LIST_INFO_QUERY_NAME = 2;
092        public static final int LIST_INFO_ARRAY_NAME = 4;
093        public static final int LIST_INFO_ARRAY_PATH = 8;
094
095        public static final int NAMECONFLICT_DEFAULT = NAMECONFLICT_OVERWRITE;  // default
096        
097        /** Optional for action = "list". Ignored by all other actions. File extension filter applied to
098        **              returned names. For example: *m. Only one mask filter can be applied at a time. */
099        private ResourceFilter filter;
100        private ResourceAndResourceNameFilter nameFilter;
101
102        private String pattern;
103        private String patternDelimiters;
104
105        /** The name of the directory to perform the action against. */
106        private Resource directory;
107
108        /** Defines the action to be taken with directory(ies) specified in directory. */
109        private String action="list";
110
111        /** Optional for action = "list". Ignored by all other actions. The query columns by which to sort 
112        **              the directory listing. Any combination of columns from query output can be specified in comma-separated list.
113        **              You can specify ASC (ascending) or DESC (descending) as qualifiers for column names. ASC is the default */
114        private String sort;
115
116        /** Used with action = "Create" to define the permissions for a directory on UNIX and Linux 
117        **              platforms. Ignored on Windows. Options correspond to the octal values of the UNIX chmod command. From 
118        **              left to right, permissions are assigned for owner, group, and other. */
119        private int mode=-1;
120
121        /** Required for action = "rename". Ignored by all other actions. The new name of the directory 
122        **              specified in the directory attribute. */
123        private String strNewdirectory;
124
125        /** Required for action = "list". Ignored by all other actions. Name of output query for directory 
126        **              listing. */
127        private String name=null;
128
129    
130    private boolean recurse=false;
131
132        private String serverPassword;
133
134        private int type=TYPE_ALL; 
135        //private boolean listOnlyNames;
136        private int listInfo=LIST_INFO_QUERY_ALL;
137        //private int acl=S3Constants.ACL_UNKNOW;
138        private Object acl=null;
139        private int storage=S3Constants.STORAGE_UNKNOW;
140        private String destination; 
141
142        private int nameconflict = NAMECONFLICT_DEFAULT;
143        
144        private boolean createPath=true;
145
146
147        @Override
148        public void release()   {
149                super.release();
150                acl=null;
151                storage=S3Constants.STORAGE_UNKNOW;
152
153
154                type=TYPE_ALL;
155                filter=null;
156                nameFilter=null;
157                destination=null;
158                directory=null;
159                action="list";
160                sort=null;
161                mode=-1;
162                strNewdirectory=null;
163                name=null;
164        recurse=false;
165        serverPassword=null;
166        listInfo=LIST_INFO_QUERY_ALL;
167
168        nameconflict = NAMECONFLICT_DEFAULT;
169        createPath=true;
170
171                pattern = null;
172                patternDelimiters = null;
173        }
174
175
176        public void setCreatepath(boolean createPath)   {
177                this.createPath=createPath;
178        }
179
180
181        /**
182         *  sets a filter
183         * @param filter
184         * @throws PageException
185         **/
186        public void setFilter(Object filter) throws PageException {
187
188                if (filter instanceof UDF)
189                        this.setFilter((UDF)filter);
190                else if (filter instanceof String)
191                        this.setFilter((String)filter);
192        }
193
194        public void setFilter(UDF filter) throws PageException  {
195                this.filter=nameFilter=UDFFilter.createResourceAndResourceNameFilter(filter);
196        }
197
198        public void setFilter(String pattern) {
199                this.pattern = pattern;
200        }
201
202        public void setFilterdelimiters(String patternDelimiters) {
203                this.patternDelimiters = patternDelimiters;
204        }
205
206        /** set the value acl
207        *  used only for s3 resources, for all others ignored
208        * @param acl value to set
209         * @throws ApplicationException 
210         * @Deprecated only exists for backward compatibility to old ra files.
211        **/
212        public void setAcl(String acl) throws ApplicationException      {
213                this.acl=acl;
214                /*acl=acl.trim().toLowerCase();
215                                
216                if("private".equals(acl))                                       this.acl=S3Constants.ACL_PRIVATE;
217                else if("public-read".equals(acl))                      this.acl=S3Constants.ACL_PRIVATE;
218                else if("public-read-write".equals(acl))        this.acl=S3Constants.ACL_PUBLIC_READ_WRITE;
219                else if("authenticated-read".equals(acl))       this.acl=S3Constants.ACL_AUTH_READ;
220                
221                else throw new ApplicationException("invalid value for attribute acl ["+acl+"]",
222                                "valid values are [private,public-read,public-read-write,authenticated-read]");*/
223        }
224        
225        public void setAcl(Object acl)  {
226                this.acl=acl;
227        }
228        
229        public void setStoreacl(Object acl)     {
230                this.acl=acl;
231        }
232        
233        /** set the value storage
234        *  used only for s3 resources, for all others ignored
235        * @param storage value to set
236         * @throws PageException 
237        **/
238        public void setStorage(String storage) throws PageException     {
239                try {
240                        this.storage=S3.toIntStorage(storage);
241                } catch (S3Exception e) {
242                        throw Caster.toPageException(e);
243                }
244        }
245        public void setStorelocation(String storage) throws PageException       {
246                setStorage(storage);
247        }
248        
249        
250        
251        public void setServerpassword(String serverPassword)    {
252            this.serverPassword=serverPassword;
253        }
254
255        
256        public void setListinfo(String strListinfo)     {
257                strListinfo=strListinfo.trim().toLowerCase();
258                this.listInfo="name".equals(strListinfo)?LIST_INFO_QUERY_NAME:LIST_INFO_QUERY_ALL;
259        }
260        
261        
262
263        /** set the value directory
264        *  The name of the directory to perform the action against.
265        * @param directory value to set
266        **/
267        public void setDirectory(String directory)      {
268                
269        this.directory=ResourceUtil.toResourceNotExisting(pageContext, directory);
270        //print.ln(this.directory);
271        }
272
273        /** set the value action
274        *  Defines the action to be taken with directory(ies) specified in directory.
275        * @param action value to set
276        **/
277        public void setAction(String action)    {
278                this.action=action.toLowerCase();
279        }
280
281        /** set the value sort
282        *  Optional for action = "list". Ignored by all other actions. The query columns by which to sort 
283        *               the directory listing. Any combination of columns from query output can be specified in comma-separated list.
284        *               You can specify ASC (ascending) or DESC (descending) as qualifiers for column names. ASC is the default
285        * @param sort value to set
286        **/
287        public void setSort(String sort)        {
288            if(sort.trim().length()>0)
289                this.sort=sort;
290        }
291
292        /** set the value mode
293        *  Used with action = "Create" to define the permissions for a directory on UNIX and Linux 
294        *               platforms. Ignored on Windows. Options correspond to the octal values of the UNIX chmod command. From 
295        *               left to right, permissions are assigned for owner, group, and other.
296        * @param mode value to set
297         * @throws PageException 
298        **/
299        public void setMode(String mode) throws PageException   {
300                try {
301                        this.mode=ModeUtil.toOctalMode(mode);
302                } 
303                catch (IOException e) {
304                        throw Caster.toPageException(e);
305                }
306        }
307
308        /** set the value newdirectory
309        *  Required for action = "rename". Ignored by all other actions. The new name of the directory 
310        *               specified in the directory attribute.
311        * @param newdirectory value to set
312        **/
313        public void setNewdirectory(String newdirectory)        {
314                //this.newdirectory=ResourceUtil.toResourceNotExisting(pageContext ,newdirectory);
315                this.strNewdirectory=newdirectory;
316        }
317        public void setDestination(String destination)  {
318                this.destination=destination;
319        }
320
321        /** set the value name
322        *  Required for action = "list". Ignored by all other actions. Name of output query for directory 
323        *               listing.
324        * @param name value to set
325        **/
326        public void setName(String name)        {
327                this.name=name;
328        }
329    
330    /**
331     * @param recurse The recurse to set.
332     */
333    public void setRecurse(boolean recurse) {
334        this.recurse = recurse;
335    }
336
337    /** set the value nameconflict
338        * Action to take if destination directory is the same as that of a file in the directory.
339        * @param nameconflict value to set
340        * @throws ApplicationException
341        **/
342        public void setNameconflict(String nameconflict) throws ApplicationException    {
343
344                this.nameconflict = FileUtil.toNameConflict( nameconflict, NAMECONFLICT_UNDEFINED | NAMECONFLICT_ERROR | NAMECONFLICT_OVERWRITE, NAMECONFLICT_DEFAULT );
345        }
346
347
348        @Override
349        public int doStartTag() throws PageException    {
350
351                if (this.filter == null && !StringUtil.isEmpty(this.pattern))
352                        this.filter = nameFilter = new WildcardPatternFilter(pattern, patternDelimiters);
353
354            //securityManager = pageContext.getConfig().getSecurityManager();
355                if(action.equals("list")) {
356                        Object res=actionList(pageContext,directory,serverPassword,type,filter,nameFilter,listInfo,recurse,sort);
357                        if(!StringUtil.isEmpty(name) && res!=null)pageContext.setVariable(name,res);
358                }
359                else if(action.equals("create")) actionCreate(pageContext,directory,serverPassword,createPath,mode,acl,storage, nameconflict);
360                else if(action.equals("delete")) actionDelete(pageContext,directory,recurse,serverPassword);
361                else if(action.equals("forcedelete")) actionDelete(pageContext,directory,true,serverPassword);
362                else if(action.equals("rename")) actionRename(pageContext,directory,strNewdirectory,serverPassword,createPath,acl,storage);
363                else if(action.equals("copy")) {
364                        if(StringUtil.isEmpty(destination,true) && !StringUtil.isEmpty(strNewdirectory,true)) {
365                                destination=strNewdirectory.trim();
366                        }
367                        actionCopy(pageContext,directory,destination,serverPassword,createPath,acl,storage,filter,recurse, nameconflict);
368                }
369                else throw new ApplicationException("invalid action ["+action+"] for the tag directory");
370                        
371                return SKIP_BODY;
372        }
373
374
375        @Override
376        public int doEndTag()   {
377                return EVAL_PAGE;
378        }
379
380        /**
381         * list all files and directories inside a directory
382         * @throws PageException
383         */
384        public static Object actionList(PageContext pageContext,Resource directory, String serverPassword, int type,ResourceFilter filter,ResourceAndResourceNameFilter nameFilter, 
385                        int listInfo,boolean recurse,String sort) throws PageException {
386                // check directory
387                SecurityManager securityManager = pageContext.getConfig().getSecurityManager();
388                securityManager.checkFileLocation(pageContext.getConfig(),directory,serverPassword);
389            
390            if(type!=TYPE_ALL) {
391                ResourceFilter typeFilter = (type==TYPE_DIR)?DIRECTORY_FILTER:FILE_FILTER;
392                if(filter==null) filter=typeFilter; 
393                else filter=new AndResourceFilter(new ResourceFilter[]{typeFilter,filter});
394            }
395            
396            
397                // create query Object
398            String[] names = new String[]{"name","size","type","dateLastModified","attributes","mode","directory"};
399            String[] types=new String[]{"VARCHAR","DOUBLE","VARCHAR","DATE","VARCHAR","VARCHAR","VARCHAR"};
400            
401            boolean hasMeta=directory instanceof ResourceMetaData;
402            if(hasMeta){
403                names = new String[]{"name","size","type","dateLastModified","attributes","mode","directory","meta"};
404                    types=new String[]{"VARCHAR","DOUBLE","VARCHAR","DATE","VARCHAR","VARCHAR","VARCHAR","OBJECT"};
405            }
406            Array array=null;
407            Query query=null;
408            Object rtn;
409            if(listInfo==LIST_INFO_QUERY_ALL || listInfo==LIST_INFO_QUERY_NAME){
410                boolean listOnlyNames=listInfo==LIST_INFO_QUERY_NAME;
411                rtn=query=new QueryImpl(
412                                listOnlyNames?new String[]{"name"}:names,
413                                listOnlyNames?new String[]{"VARCHAR"}:types,
414                                0,"query");
415            }
416            else 
417                rtn=array=new ArrayImpl();
418            
419            if(!directory.exists()){
420                if(directory instanceof FileResource) return rtn;
421                        throw new ApplicationException("directory ["+directory.toString()+"] doesn't exist");
422                }
423                if(!directory.isDirectory()){
424                        if(directory instanceof FileResource) return rtn;
425                        throw new ApplicationException("file ["+directory.toString()+"] exists, but isn't a directory");
426                }
427                if(!directory.isReadable()){
428                        if(directory instanceof FileResource) return rtn;
429                        throw new ApplicationException("no access to read directory ["+directory.toString()+"]");
430                }
431                
432                long startNS=System.nanoTime();
433                
434                try {
435                        // Query All
436                        if(listInfo==LIST_INFO_QUERY_ALL)
437                                _fillQueryAll(query,directory,filter,0,hasMeta,recurse);
438                        
439                        // Query Name
440                        else if(listInfo==LIST_INFO_QUERY_NAME) {
441                   if(recurse || type!=TYPE_ALL)_fillQueryNamesRec("",query, directory, filter, 0,recurse);
442                   else _fillQueryNames(query, directory, nameFilter, 0);
443                        }
444                        
445                        //Array Name/Path
446                        else if(listInfo==LIST_INFO_ARRAY_NAME || listInfo==LIST_INFO_ARRAY_PATH) {
447                                boolean onlyName=listInfo==LIST_INFO_ARRAY_NAME;
448                   if(!onlyName || recurse || type!=TYPE_ALL)_fillArrayPathOrName(array, directory, nameFilter, 0, recurse, onlyName);//QueryNamesRec("",query, directory, filter, 0,recurse);
449                   else _fillArrayName(array, directory, nameFilter, 0);
450                        }       
451                         
452                   
453        } catch (IOException e) {
454            throw Caster.toPageException(e);
455        }
456                
457                // sort
458                if(sort!=null && query!=null) {
459                        String[] arr=sort.toLowerCase().split(",");
460                        for(int i=arr.length-1;i>=0;i--) {
461                                try {
462                                        String[] col=arr[i].trim().split("\\s+");
463                                        if(col.length==1)query.sort(col[0].trim());
464                                        else if(col.length==2) {
465                                                String order=col[1].toLowerCase().trim();
466                                                if(order.equals("asc"))
467                                                        query.sort(col[0],lucee.runtime.type.Query.ORDER_ASC);
468                                                else if(order.equals("desc"))
469                                                        query.sort(col[0],lucee.runtime.type.Query.ORDER_DESC);
470                                                else 
471                                                        throw new ApplicationException("invalid order type ["+col[1]+"]");
472                                        }
473                                }
474                                catch(Throwable t) {
475                                        ExceptionUtil.rethrowIfNecessary(t);
476                                }
477                        }               
478                }
479                if(query!=null)query.setExecutionTime(System.nanoTime()-startNS);
480                return rtn; 
481        }
482        
483        
484
485
486    private static int _fillQueryAll(Query query, Resource directory, ResourceFilter filter, int count, boolean hasMeta, boolean recurse) throws PageException, IOException {
487        //long start=System.currentTimeMillis();
488        Resource[] list=directory.listResources();
489        
490                if(list==null || list.length==0) return count;
491        String dir=directory.getCanonicalPath();
492        //      fill data to query
493        //query.addRow(list.length);
494        boolean isDir;
495        for(int i=0;i<list.length;i++) {
496            if(filter==null || filter.accept(list[i])) {
497                query.addRow(1);
498                count++;
499                query.setAt(KeyConstants._name,count,list[i].getName());
500                isDir=list[i].isDirectory();
501                query.setAt(KeyConstants._size,count,new Double(isDir?0:list[i].length()));
502                query.setAt(KeyConstants._type,count,isDir?"Dir":"File");
503                if(directory.getResourceProvider().isModeSupported()){
504                                
505                        query.setAt(MODE,count,new ModeObjectWrap(list[i]));
506                }
507                query.setAt(DATE_LAST_MODIFIED,count,new Date(list[i].lastModified()));
508                query.setAt(ATTRIBUTES,count,getFileAttribute(list[i],true));
509                
510                if(hasMeta){
511                        query.setAt(META,count,((ResourceMetaData)list[i]).getMetaData());
512                }
513                
514                        query.setAt(DIRECTORY,count,dir);
515            }   
516            if(recurse && list[i].isDirectory())
517                count=_fillQueryAll(query,list[i],filter,count,hasMeta,recurse);
518        }
519        return count;
520    }
521 // this method only exists for performance reasion
522    private static int _fillQueryNames(Query query, Resource directory, ResourceNameFilter filter, int count) throws PageException {
523        String[] list=directory.list();
524        if(list==null || list.length==0) return count;
525        for(int i=0;i<list.length;i++) {
526            if(filter==null || filter.accept(directory,list[i])) {
527                query.addRow(1);
528                count++;
529                query.setAt(KeyConstants._name,count,list[i]);  
530            }     
531        }
532        return count;
533    }
534    
535    private static int _fillQueryNamesRec(String parent, Query query, Resource directory, ResourceFilter filter, int count, boolean recurse) throws PageException {
536        Resource[] list=directory.listResources();
537        if(list==null || list.length==0) return count;
538        for(int i=0;i<list.length;i++) {
539            if(filter==null || filter.accept(list[i])) {
540                query.addRow(1);
541                count++;
542                query.setAt(KeyConstants._name,count,parent.concat(list[i].getName()));
543                
544            } 
545            if(recurse && list[i].isDirectory())
546                count=_fillQueryNamesRec(parent + list[i].getName() + "/", query, list[i], filter, count, recurse);
547        }
548        return count;
549    }
550    
551    private static int _fillArrayPathOrName(Array arr, Resource directory, ResourceFilter filter, int count, boolean recurse,boolean onlyName) throws PageException {
552        Resource[] list=directory.listResources();
553        if(list==null || list.length==0) return count;
554        for(int i=0;i<list.length;i++) {
555            if(filter==null || filter.accept(list[i])) {
556                arr.appendEL(onlyName?list[i].getName():list[i].getAbsolutePath());
557                count++;
558                
559            } 
560            if(recurse && list[i].isDirectory())
561                count=_fillArrayPathOrName(arr,list[i],filter,count,recurse,onlyName);  
562        }
563        return count;
564    }
565    
566    // this method only exists for performance reasion
567    private static int _fillArrayName(Array arr, Resource directory, ResourceNameFilter filter, int count) {
568        String[] list=directory.list();
569        if(list==null || list.length==0) return count;
570        for(int i=0;i<list.length;i++) {
571            if(filter==null || filter.accept(directory,list[i])) {
572                arr.appendEL(list[i]);  
573            }     
574        }
575        return count;
576    }
577
578
579
580    /**
581         * create a directory
582         * @throws PageException 
583         */
584    public static void actionCreate(PageContext pc,Resource directory,String serverPassword, boolean createPath, int mode, Object acl, int storage, int nameConflict) throws PageException {
585
586        SecurityManager securityManager = pc.getConfig().getSecurityManager();
587            securityManager.checkFileLocation(pc.getConfig(),directory,serverPassword);
588            
589                if(directory.exists()) {
590                        if(directory.isDirectory()) {
591                                if ( nameConflict == NAMECONFLICT_SKIP )
592                                        return;
593
594                                throw new ApplicationException("directory ["+directory.toString()+"] already exist");
595                        }
596                        else if(directory.isFile())
597                                throw new ApplicationException("can't create directory ["+directory.toString()+"], it exist a file with same name");
598                }
599                //if(!directory.mkdirs())       throw new ApplicationException("can't create directory ["+directory.toString()+"]");
600                try {
601                        directory.createDirectory(createPath);
602                } catch (IOException ioe) {
603                        throw Caster.toPageException(ioe);
604                }
605                
606                // set S3 stuff
607                setS3Attrs(directory,acl,storage);
608            
609                // Set Mode
610                if(mode!=-1) {
611                        try {
612                directory.setMode(mode);
613                //FileUtil.setMode(directory,mode);
614            } catch (IOException e) {
615                throw Caster.toPageException(e);
616            }
617                }
618        }
619        
620        private static void setS3Attrs(Resource res,Object acl,int storage) throws PageException {
621                String scheme = res.getResourceProvider().getScheme();
622                
623                if("s3".equalsIgnoreCase(scheme)){
624                        S3Resource s3r=(S3Resource) res;
625                        if(acl!=null){
626                                try {
627                                        // old way
628                                        if(Decision.isString(acl)) {
629                                                if(Decision.isInteger(acl)) s3r.setACL(Caster.toIntValue(acl));
630                                                else s3r.setACL(S3.toIntACL(Caster.toString(acl)));
631                                        }
632                                        // new way
633                                        else {
634                                                StoreSetACL.invoke(s3r, acl);
635                                        }
636                                } catch (IOException e) {
637                                        throw Caster.toPageException(e);
638                                }
639                        }
640                        
641                        if(storage!=S3Constants.STORAGE_UNKNOW) s3r.setStorage(storage);
642                }
643        }
644
645
646
647        /**
648         * delete directory
649         * @param dir
650         * @param forceDelete
651         * @throws PageException 
652         */
653        public static void actionDelete(PageContext pc,Resource dir, boolean forceDelete,String serverPassword) throws PageException {
654                SecurityManager securityManager = pc.getConfig().getSecurityManager();
655            securityManager.checkFileLocation(pc.getConfig(),dir,serverPassword);
656            
657                // directory doesn't exist
658                if(!dir.exists()) {
659                        if(dir.isDirectory())
660                                throw new ApplicationException("directory ["+dir.toString()+"] doesn't exist");
661                        else if(dir.isFile())
662                                throw new ApplicationException("file ["+dir.toString()+"] doesn't exist and isn't a directory");
663                }
664                
665                // check if file
666                if(dir.isFile())
667                        throw new ApplicationException("can't delete ["+dir.toString()+"], it isn't a directory it is a file");
668                
669                // delete directory
670                try {
671                        dir.remove(forceDelete);
672                } catch (IOException e) {
673                        throw Caster.toPageException(e);
674                }
675        }
676
677        /**
678         * rename a directory to a new Name
679         * @throws PageException 
680         */
681        public static  void actionRename(PageContext pc,Resource directory,String strNewdirectory,String serverPassword, boolean createPath, Object acl,int storage) throws PageException {
682                // check directory
683                SecurityManager securityManager = pc.getConfig().getSecurityManager();
684            securityManager.checkFileLocation(pc.getConfig(),directory,serverPassword);
685                
686            
687                if(!directory.exists())
688                        throw new ApplicationException("the directory ["+directory.toString()+"] doesn't exist");
689                if(!directory.isDirectory())
690                        throw new ApplicationException("the file ["+directory.toString()+"] exists, but it isn't a directory");
691                if(!directory.canRead())
692                        throw new ApplicationException("no access to read directory ["+directory.toString()+"]");
693                
694                if(strNewdirectory==null)
695                        throw new ApplicationException("the attribute [newDirectory] is not defined");
696                
697                // real to source 
698                Resource newdirectory=toDestination(pc,strNewdirectory,directory);
699                
700            securityManager.checkFileLocation(pc.getConfig(),newdirectory,serverPassword);
701                if(newdirectory.exists())
702                        throw new ApplicationException("new directory ["+newdirectory.toString()+"] already exists");
703                if(createPath) {
704                        newdirectory.getParentResource().mkdirs();
705                        
706                }
707                try {
708                        directory.moveTo(newdirectory);
709                }
710                catch(Throwable t) {
711                        throw Caster.toPageException(t);
712                }
713                
714                // set S3 stuff
715                setS3Attrs(directory,acl,storage);
716            
717        }
718        
719        
720        public static  void actionCopy(PageContext pc,Resource directory,String strDestination,String serverPassword,boolean createPath, Object acl,int storage, final ResourceFilter filter, boolean recurse, int nameconflict) throws PageException {
721                // check directory
722                SecurityManager securityManager = pc.getConfig().getSecurityManager();
723            securityManager.checkFileLocation(pc.getConfig(),directory,serverPassword);
724                
725            
726                if(!directory.exists())
727                        throw new ApplicationException("directory ["+directory.toString()+"] doesn't exist");
728                if(!directory.isDirectory())
729                        throw new ApplicationException("file ["+directory.toString()+"] exists, but isn't a directory");
730                if(!directory.canRead())
731                        throw new ApplicationException("no access to read directory ["+directory.toString()+"]");
732                
733                if(StringUtil.isEmpty(strDestination))
734                        throw new ApplicationException("attribute destination is not defined");
735                
736                // real to source 
737                Resource newdirectory=toDestination(pc,strDestination,directory);
738                
739                if ( nameconflict == NAMECONFLICT_ERROR && newdirectory.exists() )
740                        throw new ApplicationException("new directory ["+newdirectory.toString()+"] already exist");
741                
742            securityManager.checkFileLocation(pc.getConfig(),newdirectory,serverPassword);
743
744                try {
745                        boolean clearEmpty=false;
746                        // has already a filter
747                        ResourceFilter f=null;
748                        if(filter!=null) {
749                                if(!recurse) {
750                                        f=new AndResourceFilter(
751                                                        new ResourceFilter[]{
752                                                                        filter,
753                                                                        new NotResourceFilter(DirectoryResourceFilter.FILTER)
754                                                                }
755                                                        );
756                                }
757                                else {
758                                        clearEmpty=true;
759                                        f=new OrResourceFilter(
760                                                        new ResourceFilter[]{
761                                                                        filter,
762                                                                        DirectoryResourceFilter.FILTER
763                                                                }
764                                                        );
765                                }
766                        }
767                        else {
768                                if(!recurse)f=new NotResourceFilter(DirectoryResourceFilter.FILTER);
769                        }
770                        if(!createPath) {
771                                Resource p = newdirectory.getParentResource();
772                                if(p!=null && !p.exists())
773                                        throw new ApplicationException("parent directory for ["+newdirectory+"] doesn't exist");
774                        }
775                        ResourceUtil.copyRecursive(directory, newdirectory,f);
776                        if(clearEmpty)ResourceUtil.removeEmptyFolders(newdirectory,f==null?null:new NotResourceFilter(filter));
777                        
778                }
779                catch(Throwable t) {
780                        ExceptionUtil.rethrowIfNecessary(t);
781                        throw new ApplicationException(t.getMessage());
782                }
783                
784                // set S3 stuff
785                setS3Attrs(directory,acl,storage);
786            
787        }
788
789        
790        private static Resource toDestination(PageContext pageContext,String path, Resource source) {
791                if(source!=null && path.indexOf(File.separatorChar)==-1 && path.indexOf('/')==-1 && path.indexOf('\\')==-1) {
792                        Resource p = source.getParentResource();
793                        if(p!=null)return p.getRealResource(path);
794                }
795                return ResourceUtil.toResourceNotExisting(pageContext ,path);
796        }
797
798
799        private static String getFileAttribute(Resource file, boolean exists){
800                return  exists && !file.isWriteable() ? "R".concat(file.isHidden() ? "H" : "") : file.isHidden() ? "H" : "";
801        }
802
803
804        /**
805         * @param strType the type to set
806         */
807        public void setType(String strType) throws ApplicationException {
808                strType=strType.trim().toLowerCase();
809                
810                if("all".equals(strType)) type=TYPE_ALL;
811                else if("dir".equals(strType)) type=TYPE_DIR;
812                else if("directory".equals(strType)) type=TYPE_DIR;
813                else if("file".equals(strType)) type=TYPE_FILE;
814                else throw new ApplicationException("invalid type ["+strType+"] for the tag directory");
815                        
816        }
817
818}