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 }