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