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