001    package railo.commons.io.res.type.ftp;
002    
003    
004    import java.io.ByteArrayInputStream;
005    import java.io.IOException;
006    import java.io.InputStream;
007    import java.io.OutputStream;
008    import java.util.ArrayList;
009    import java.util.Calendar;
010    import java.util.List;
011    
012    import org.apache.commons.net.ftp.FTP;
013    import org.apache.commons.net.ftp.FTPFile;
014    
015    import railo.commons.date.JREDateTimeUtil;
016    import railo.commons.io.IOUtil;
017    import railo.commons.io.ModeUtil;
018    import railo.commons.io.res.Resource;
019    import railo.commons.io.res.ResourceProvider;
020    import railo.commons.io.res.util.ResourceSupport;
021    import railo.commons.io.res.util.ResourceUtil;
022    import railo.commons.lang.StringUtil;
023    import railo.runtime.PageContext;
024    import railo.runtime.engine.ThreadLocalPageContext;
025    import railo.runtime.op.Caster;
026    
027    public final class FTPResource extends ResourceSupport {
028    
029    
030            private final FTPResourceProvider provider;
031            private final String path;
032            private final String name;
033            private final FTPConnectionData data;
034            
035    
036            
037            /**
038             * Constructor of the class
039             * @param factory
040             * @param data 
041             * @param path
042             */
043             FTPResource(FTPResourceProvider provider, FTPConnectionData data, String path) {
044                    this.provider=provider;
045                    this.data=data;
046                    
047                    String[] pathName=ResourceUtil.translatePathName(path);
048                    this.path=pathName[0];
049                    this.name=pathName[1];
050            }
051    
052             /**
053             * Constructor of the class
054             * @param factory
055             * @param data 
056             * @param path
057             */
058             private FTPResource(FTPResourceProvider provider, FTPConnectionData data, String path,String name) {
059                    this.provider=provider;
060                    this.data=data;
061                    this.path=path;
062                    this.name=name;
063            }
064    
065            /**
066             * @see railo.commons.io.res.Resource#isReadable()
067             */
068            public boolean isReadable() {
069                    Boolean rtn = hasPermission(FTPFile.READ_PERMISSION);
070                    if(rtn==null) return false;
071                    return rtn.booleanValue();
072            }
073    
074            public boolean isWriteable() {
075                    Boolean rtn = hasPermission(FTPFile.WRITE_PERMISSION);
076                    if(rtn==null) return false;
077                    return rtn.booleanValue();
078            }
079    
080            private Boolean hasPermission(int permission) {
081                    FTPResourceClient client=null;
082                    try {
083                            provider.read(this);
084                            client=provider.getClient(data);
085                            FTPFile file=client.getFTPFile(this);
086                            if(file==null) return null;
087                            return  Caster.toBoolean(file.hasPermission(FTPFile.USER_ACCESS,permission) || 
088                                            file.hasPermission(FTPFile.GROUP_ACCESS,permission) || 
089                                            file.hasPermission(FTPFile.WORLD_ACCESS,permission));
090                    } 
091                    catch (IOException e) {
092                            return Boolean.FALSE;
093                    }
094                    finally {
095                            provider.returnClient(client);
096                    }
097            }
098            
099            /**
100             * @see railo.commons.io.res.Resource#remove(boolean)
101             */
102            public void remove(boolean alsoRemoveChildren) throws IOException {
103                    if(isRoot()) throw new FTPResoucreException("can't delete root of ftp server");
104                    
105                    if(alsoRemoveChildren)ResourceUtil.removeChildren(this);
106                    FTPResourceClient client=null;
107                    try {
108                            provider.lock(this);
109                            client = provider.getClient(data);
110                            boolean result = client.deleteFile(getInnerPath());
111                            if(!result) throw new IOException("can't delete file ["+getPath()+"]");
112                    }
113                    finally {
114                            provider.returnClient(client);
115                            provider.unlock(this);
116                    }
117            }
118    
119            /**
120             * @see railo.commons.io.res.util.ResourceSupport#delete()
121             */
122            public boolean delete() {
123                    if(isRoot()) return false;
124                    FTPResourceClient client = null;
125                    try {
126                            provider.lock(this);
127                            client = provider.getClient(data);
128                            return client.deleteFile(getInnerPath());
129                    } 
130                    catch (IOException e) {
131                            return false;
132                    }
133                    finally {
134                            provider.returnClient(client);
135                            provider.unlock(this);
136                    }
137            }
138    
139            /**
140             * @see res.Resource#exists()
141             */
142            public boolean exists() {
143                    try {
144                            provider.read(this);
145                    } catch (IOException e) {
146                            return true;
147                    }
148                    FTPResourceClient client = null;
149                    InputStream is=null;
150                    try {
151    //                       getClient muss zuerst sein so wird verbindung gepr゚ft
152                            client = provider.getClient(data);
153                            if(isRoot()) return true;
154    
155                            FTPFile file = client.getFTPFile(this);
156                            if(file!=null) {
157                                    return !file.isUnknown();
158                            }
159                            
160                            //String pathname = getInnerPath();
161                            String p = getInnerPath();
162                            if(!StringUtil.endsWith(p, '/'))p+="/";
163                            if(client.listNames(p)!=null) return true;
164                            return false;
165                    } 
166                    catch (IOException e) {
167                            return false;
168                    }
169                    finally {
170                            IOUtil.closeEL(is);
171                            provider.returnClient(client);
172                    }
173            }
174    
175            /**
176             * @see res.Resource#getFullName()
177             */
178            public String getName() {
179                    return name;
180            }
181    
182            /**
183             * @see res.Resource#getParent()
184             */
185            public String getParent() {
186                    if(isRoot()) return null;
187                    return provider.getScheme().concat("://").concat(data.key()).concat(path.substring(0,path.length()-1));
188            } 
189            
190            public String getInnerParent() {
191                    return path;
192            }
193    
194            /**
195             * @see res.Resource#getParentResource()
196             */
197            public Resource getParentResource() {
198                    if(isRoot()) return null;
199                    return new FTPResource(provider,data,path);
200            }
201    
202            /**
203             * @see railo.commons.io.res.Resource#getRealResource(java.lang.String)
204             */
205            public Resource getRealResource(String realpath) {
206                    realpath=ResourceUtil.merge(getInnerPath(), realpath);
207                    if(realpath.startsWith("../"))return null;
208                    return new FTPResource(provider,data,realpath);
209            }
210    
211            /**
212             * @see res.Resource#getPath()
213             */
214            public String getPath() {       
215                    return provider.getScheme().concat("://").concat(data.key()).concat(path).concat(name);
216            }
217            /**
218             * @return returns path starting from ftp root
219             */
220            String getInnerPath() { 
221                    return path.concat(name);
222            }
223    
224            /**
225             * @see railo.commons.io.res.Resource#isAbsolute()
226             */
227            public boolean isAbsolute() {
228                    // TODO impl isAbolute
229                    return true;
230            }
231    
232            /**
233             * @see railo.commons.io.res.Resource#isDirectory()
234             */
235            public boolean isDirectory() {
236                    try {
237                            provider.read(this);
238                    } 
239                    catch (IOException e1) {
240                            return false;
241                    }
242                    FTPResourceClient client=null;
243                    try {
244                            // getClient muss zuerst sein so wird verbindung gepr゚ft
245                            client = provider.getClient(data);
246                            if(isRoot())return true;
247    
248                            FTPFile file = client.getFTPFile(this);
249                            if(file!=null) {
250                                    return file.isDirectory();
251                            }
252                            //if(file==null) return false;
253                            //return file.isDirectory();
254                            
255                            String p = getInnerPath();
256                            if(!StringUtil.endsWith(p, '/'))p+="/";
257                            return client.listNames(p)!=null;
258                            
259                    } 
260                    catch (IOException e) {
261                            return false;
262                    }
263                    finally {
264                            provider.returnClient(client);
265                    }
266            }
267    
268            /**
269             * @see res.Resource#isFile()
270             */
271            public boolean isFile() {
272                    if(isRoot()) return false;
273                    try {
274                            provider.read(this);
275                    } 
276                    catch (IOException e1) {
277                            return false;
278                    }
279                    FTPResourceClient client=null;
280                    InputStream is=null;
281                    try {
282                            client = provider.getClient(data);
283                            FTPFile file = client.getFTPFile(this);
284                            if(file!=null) {
285                                    return file.isFile();
286                            }
287                            return false;
288                            //String pathname = getInnerPath();
289                            //return (is=client.retrieveFileStream(pathname))!=null;
290                    } 
291                            
292                            
293    
294                    catch (IOException e) {
295                            return false;
296                    }
297                    finally {
298                            IOUtil.closeEL(is);
299                            provider.returnClient(client);
300                    }
301            }
302    
303            /**
304             * @see railo.commons.io.res.Resource#lastModified()
305             */
306            public long lastModified() {
307                    //if(isRoot()) return 0;
308                    
309                    FTPResourceClient client=null;
310                    try {
311                            provider.read(this);
312                            client=provider.getClient(data);
313                            FTPFile file = client.getFTPFile(this);
314                            if(file==null) return 0;
315                            return file.getTimestamp().getTimeInMillis();
316                    }
317                    catch (IOException e) {
318                            return 0;
319                    }
320                    finally {
321                            provider.returnClient(client);
322                    }
323            }
324    
325            /**
326             * @see railo.commons.io.res.Resource#length()
327             */
328            public long length() {
329                    if(isRoot()) return 0;
330                    FTPResourceClient client=null;
331                    try {
332                            provider.read(this);
333                            client = provider.getClient(data);
334                            FTPFile file = client.getFTPFile(this);
335                            if(file==null) return 0;
336                            return file.getSize();
337                    } 
338                    catch (IOException e) {
339                            return 0;
340                    }
341                    finally {
342                            provider.returnClient(client);
343                    }
344            }
345    
346            /**
347             * @see res.Resource#listResources()
348             */
349            public Resource[] listResources() {
350                    if(isFile()) return null;//new Resource[0];
351                    
352                    FTPResourceClient client=null;
353                    try {
354                            client = provider.getClient(data);
355                            FTPFile[] files=null;
356                            String p = getInnerPath();
357                            if(!StringUtil.endsWith(p, '/'))p+="/";
358                            files=client.listFiles(p);
359                            if(files==null) return new Resource[0];
360                            
361                            List list=new ArrayList();
362                            String parent=path.concat(name).concat("/");
363                            String name;
364                            FTPResource res;
365                        for(int i=0;i<files.length;i++) {
366                            name=files[i].getName();
367                            if(!".".equals(name) && !"..".equals(name)) {
368                                    res=new FTPResource(provider,data,parent,name);
369                                    client.registerFTPFile(res, files[i]);
370                                    list.add(res);
371                            }
372                        }
373                            return (Resource[]) list.toArray(new FTPResource[list.size()]);
374                    }
375                    catch(IOException ioe) {
376                            return null;
377                    }
378                    finally {
379                            provider.returnClient(client);
380                    }
381            }
382    
383            /**
384             * @see railo.commons.io.res.Resource#setLastModified(long)
385             */
386            public boolean setLastModified(long time) {
387                    //if(isRoot()) return false;
388                    
389                    FTPResourceClient client=null;
390                    try {
391                            provider.lock(this);
392                            client=provider.getClient(data);
393                            
394                            PageContext pc = ThreadLocalPageContext.get();
395                            Calendar c;
396                            if(pc==null) c=JREDateTimeUtil.getCalendar();
397                            else c=JREDateTimeUtil.getCalendar(pc.getTimeZone());
398                            c.setTimeInMillis(time);
399                            FTPFile file = client.getFTPFile(this);
400                            if(file==null) return false;
401                            file.setTimestamp(c);
402                            client.unregisterFTPFile(this);
403                            return true;
404                    }
405                    catch (IOException e) {}
406                    finally {
407                            provider.returnClient(client);
408                            provider.unlock(this);
409                    }
410    
411                    return false;
412            }
413    
414            public boolean setReadOnly() {
415                    try {
416                            setMode(ModeUtil.setWritable(getMode(), false));
417                            return true;
418                    } catch (IOException e) {
419                            return false;
420                    }
421            }
422    
423            /**
424             * @see res.Resource#createFile(boolean)
425             */
426            public void createFile(boolean createParentWhenNotExists) throws IOException {
427                    ResourceUtil.checkCreateFileOK(this, createParentWhenNotExists);
428                    //client.unregisterFTPFile(this);
429                    IOUtil.copy(new ByteArrayInputStream(new byte[0]), getOutputStream(), true, true);
430            }
431            
432            /**
433             * @see res.Resource#moveTo(res.Resource)
434             */
435            public void moveTo(Resource dest) throws IOException {
436                    FTPResourceClient client=null;
437                    ResourceUtil.checkMoveToOK(this, dest);
438                    try {
439                            provider.lock(this);
440                            client = provider.getClient(data);
441                            
442                            client.unregisterFTPFile(this);
443                            
444                            if(dest instanceof FTPResource) moveTo(client,(FTPResource)dest);
445                            else super.moveTo(dest);
446                            
447                    }
448                    finally {
449                            provider.returnClient(client);
450                            provider.unlock(this);
451                    }
452            }
453    
454            private void moveTo(FTPResourceClient client, FTPResource dest) throws IOException {
455                    if(!dest.data.equals(data)) {
456                            super.moveTo(dest);
457                            return;
458                    }
459                    if(dest.exists())dest.delete();
460                    
461                    client.unregisterFTPFile(dest);
462                    boolean ok = client.rename(getInnerPath(), dest.getInnerPath());
463                    if(!ok) throw new IOException("can't create file "+this);
464                    
465            }
466    
467            /**
468             * @see res.Resource#createDirectory(boolean)
469             */
470            public void createDirectory(boolean createParentWhenNotExists) throws IOException {
471                    ResourceUtil.checkCreateDirectoryOK(this, createParentWhenNotExists);
472                    
473                    FTPResourceClient client=null;
474                    try {
475                            provider.lock(this);
476                            client = provider.getClient(data);
477                            client.unregisterFTPFile(this);
478                            boolean ok = client.makeDirectory(getInnerPath());
479                            if(!ok) throw new IOException("can't create file "+this);
480                            
481                    }
482                    finally {
483                            provider.returnClient(client);
484                            provider.unlock(this);
485                    }
486            }
487    
488            /**
489             * @throws IOException 
490             * @see res.Resource#getInputStream()
491             */
492            public InputStream getInputStream() throws IOException {
493                    ResourceUtil.checkGetInputStreamOK(this);
494                    provider.lock(this);
495                    FTPResourceClient client=provider.getClient(data);
496                    client.setFileType(FTP.BINARY_FILE_TYPE);
497                    try {
498                            return IOUtil.toBufferedInputStream(new FTPResourceInputStream(client,this,client.retrieveFileStream(getInnerPath())));
499                    } 
500                    catch (IOException e) {
501                            provider.returnClient(client);
502                            provider.unlock(this);
503                            throw e;
504                    }
505            }
506    
507            /**
508             * @see railo.commons.io.res.Resource#getOutputStream(boolean)
509             */
510            public OutputStream getOutputStream(boolean append) throws IOException {
511                    ResourceUtil.checkGetOutputStreamOK(this);
512                    FTPResourceClient client=null;
513                    try {
514                            provider.lock(this);
515                            client=provider.getClient(data);
516                            client.unregisterFTPFile(this);
517                            client.setFileType(FTP.BINARY_FILE_TYPE);
518                            OutputStream os = append?client.appendFileStream(getInnerPath()):client.storeFileStream(getInnerPath());
519                            if(os==null)throw new IOException("can not open stream to file ["+this+"]");
520                            
521                            return IOUtil.toBufferedOutputStream(new FTPResourceOutputStream(client,this,os));
522                    }
523                    catch (IOException e) {
524                            provider.returnClient(client);
525                            provider.unlock(this);
526                            throw e;
527                    }
528            }
529    
530    
531            /**
532             * @see res.util.ResourceSupport#list()
533             */
534            public String[] list() {
535                    if(isFile()) return new String[0];
536                    
537                    FTPResourceClient client=null;
538                    try {
539                            client = provider.getClient(data);
540                            String[] files=null;
541                            
542                            String p = getInnerPath();
543                            if(!StringUtil.endsWith(p, '/'))p+="/";
544                            files=client.listNames(p);
545                            if(files==null) return new String[0];
546                            for(int i=0;i<files.length;i++) {
547                                    files[i]=cutName(files[i]);
548                        }
549                            return files;
550                    }
551                    catch(IOException ioe) {
552                            return null;
553                    }
554                    finally {
555                            provider.returnClient(client);
556                    }
557            }
558    
559            private String cutName(String path) {
560                    int index=path.lastIndexOf('/');
561                    if(index==-1) return path;
562                    return path.substring(index+1);
563            }
564    
565            /**
566             * @see res.Resource#getResourceProvider()
567             */
568            public ResourceProvider getResourceProvider() {
569                    return provider;
570            }
571            
572            public FTPResourceProvider getFTPResourceProvider() {
573                    return provider;
574            }
575    
576            
577            boolean isRoot() {
578                    return StringUtil.isEmpty(name);
579            }
580    
581            public int getMode() {
582                    //if(isRoot()) return 0;
583                    
584                    FTPResourceClient client=null;
585                    try {
586                            provider.read(this);
587                            client=provider.getClient(data);
588                            
589                            FTPFile file = client.getFTPFile(this);
590                            int mode=0;
591                            if(file==null)return 0;
592                            
593                            // World
594                            if(file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION))        mode+=01;
595                            if(file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION))          mode+=02;
596                            if(file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION))           mode+=04;
597    
598                            // Group
599                            if(file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION))        mode+=010;
600                            if(file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION))          mode+=020;
601                            if(file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION))           mode+=040;
602    
603                            // Owner
604                            if(file.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION))         mode+=0100;
605                            if(file.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION))           mode+=0200;
606                            if(file.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION))            mode+=0400;
607                            
608                            return mode;
609                            
610                    }
611                    catch (IOException e) {}
612                    finally {
613                            provider.returnClient(client);
614                    }
615    
616                    return 0;
617            }
618    
619            public void setMode(int mode) throws IOException {
620                    //if(isRoot()) throw new IOException("can't change mode of root");
621            
622                    FTPResourceClient client=null;
623                    try {
624                            provider.lock(this);
625                            client=provider.getClient(data);
626                            
627                            FTPFile file = client.getFTPFile(this);
628                            if(file!=null) {
629                                    // World
630                                    file.setPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION,(mode&01)>0);
631                                    file.setPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION,(mode&02)>0);
632                                    file.setPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION,(mode&04)>0);
633            
634                                    // Group
635                                    file.setPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION,(mode&010)>0);
636                                    file.setPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION,(mode&020)>0);
637                                    file.setPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION,(mode&040)>0);
638            
639                                    // Owner
640                                    file.setPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION,(mode&0100)>0);
641                                    file.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION,(mode&0200)>0);
642                                    file.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION,(mode&0400)>0);
643            
644                                    client.unregisterFTPFile(this);
645                            }
646                    }
647                    catch (IOException e) {}
648                    finally {
649                            provider.returnClient(client);
650                            provider.unlock(this);
651                    }
652    
653            }
654    
655            public boolean setReadable(boolean value) {
656                    try {
657                            setMode(ModeUtil.setReadable(getMode(), value));
658                            return true;
659                    } catch (IOException e) {
660                            return false;
661                    }
662            }
663    
664            public boolean setWritable(boolean value) {
665                    try {
666                            setMode(ModeUtil.setWritable(getMode(), value));
667                            return true;
668                    } catch (IOException e) {
669                            return false;
670                    }
671            }
672    }