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 }