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.runtime.tag; 020 021import static lucee.runtime.tag.util.FileUtil.NAMECONFLICT_ERROR; 022import static lucee.runtime.tag.util.FileUtil.NAMECONFLICT_MAKEUNIQUE; 023import static lucee.runtime.tag.util.FileUtil.NAMECONFLICT_OVERWRITE; 024import static lucee.runtime.tag.util.FileUtil.NAMECONFLICT_SKIP; 025import static lucee.runtime.tag.util.FileUtil.NAMECONFLICT_UNDEFINED; 026 027import java.awt.image.BufferedImage; 028import java.io.ByteArrayInputStream; 029import java.io.File; 030import java.io.IOException; 031import java.io.InputStream; 032import java.io.UnsupportedEncodingException; 033import java.nio.charset.Charset; 034 035import lucee.commons.io.CharsetUtil; 036import lucee.commons.io.IOUtil; 037import lucee.commons.io.ModeUtil; 038import lucee.commons.io.SystemUtil; 039import lucee.commons.io.res.Resource; 040import lucee.commons.io.res.type.s3.S3; 041import lucee.commons.io.res.type.s3.S3Resource; 042import lucee.commons.io.res.util.ModeObjectWrap; 043import lucee.commons.io.res.util.ResourceUtil; 044import lucee.commons.lang.ExceptionUtil; 045import lucee.commons.lang.StringUtil; 046import lucee.commons.lang.mimetype.MimeType; 047import lucee.runtime.PageContext; 048import lucee.runtime.PageContextImpl; 049import lucee.runtime.exp.ApplicationException; 050import lucee.runtime.exp.PageException; 051import lucee.runtime.ext.tag.BodyTagImpl; 052import lucee.runtime.functions.list.ListFirst; 053import lucee.runtime.functions.list.ListLast; 054import lucee.runtime.functions.s3.StoreSetACL; 055import lucee.runtime.img.ImageUtil; 056import lucee.runtime.op.Caster; 057import lucee.runtime.op.Decision; 058import lucee.runtime.tag.util.FileUtil; 059import lucee.runtime.type.Array; 060import lucee.runtime.type.ArrayImpl; 061import lucee.runtime.type.Struct; 062import lucee.runtime.type.StructImpl; 063import lucee.runtime.type.dt.DateImpl; 064import lucee.runtime.type.dt.DateTimeImpl; 065import lucee.runtime.type.scope.Form; 066import lucee.runtime.type.scope.FormItem; 067import lucee.runtime.type.util.ArrayUtil; 068import lucee.runtime.type.util.KeyConstants; 069import lucee.runtime.type.util.ListUtil; 070 071/** 072* Handles all interactions with files. The attributes you use with cffile depend on the value of the action attribute. 073* For example, if the action = "write", use the attributes associated with writing a text file. 074* 075* 076* 077**/ 078public final class FileTag extends BodyTagImpl { 079 080 private static final int ACTION_UNDEFINED = 0; 081 private static final int ACTION_MOVE = 1; 082 private static final int ACTION_WRITE = 2; 083 private static final int ACTION_APPEND = 3; 084 private static final int ACTION_READ = 4; 085 private static final int ACTION_UPLOAD = 5; 086 private static final int ACTION_UPLOAD_ALL = 6; 087 private static final int ACTION_COPY = 7; 088 private static final int ACTION_INFO = 8; 089 private static final int ACTION_TOUCH = 9; 090 private static final int ACTION_DELETE = 10; 091 private static final int ACTION_READ_BINARY = 11; 092 //private static final Key SET_ACL = KeyImpl.intern("setACL"); 093 094 //private static final String DEFAULT_ENCODING=Charset.getDefault(); 095 096 /** Type of file manipulation that the tag performs. */ 097 private int action; 098 099 /** Absolute pathname of directory or file on web server. */ 100 private String strDestination; 101 102 /** Content of the file to be created. */ 103 private Object output; 104 105 /** Absolute pathname of file on web server. */ 106 private Resource file; 107 108 /** Applies only to Solaris and HP-UX. Permissions. Octal values of UNIX chmod command. Assigned to owner, group, and other, respectively. */ 109 private int mode=-1; 110 111 /** Name of variable to contain contents of text file. */ 112 private String variable; 113 114 /** Name of form field used to select the file. */ 115 private String filefield; 116 117 /** Character set name for the file contents. */ 118 private Charset charset=null; 119 120 /** Yes: appends newline character to text written to file */ 121 private boolean addnewline=true; 122 private boolean fixnewline=true; 123 /** One attribute (Windows) or a comma-delimited list of attributes (other platforms) to set on the file. 124 ** If omitted, the file's attributes are maintained. */ 125 private String attributes; 126 127 /** Absolute pathname of file on web server. 128 ** On Windows, use backward slashes; on UNIX, use forward slashes. */ 129 private Resource source; 130 131 /** Action to take if filename is the same as that of a file in the directory. */ 132 private int nameconflict=NAMECONFLICT_UNDEFINED; 133 134 /** Limits the MIME types to accept. Comma-delimited list. For example, to permit JPG and Microsoft Word file uploads: 135 ** accept = "image/jpg, application/msword" 136 ** The browser uses file extension to determine file type. */ 137 private String accept; 138 139 private boolean strict=true; 140 private boolean createPath=false; 141 142 private String result=null; 143 144 private lucee.runtime.security.SecurityManager securityManager; 145 146 private String serverPassword=null; 147 private Object acl=null; 148 149 @Override 150 public void release() { 151 super.release(); 152 acl=null; 153 action=ACTION_UNDEFINED; 154 strDestination=null; 155 output=null; 156 file=null; 157 mode=-1; 158 variable=null; 159 filefield=null; 160 charset=null; 161 addnewline=true; 162 fixnewline=true; 163 attributes=null; 164 source=null; 165 nameconflict=NAMECONFLICT_UNDEFINED; 166 accept=null; 167 strict=true; 168 createPath=false; 169 securityManager=null; 170 result=null; 171 serverPassword=null; 172 } 173 174 /** set the value action 175 * Type of file manipulation that the tag performs. 176 * @param strAction value to set 177 **/ 178 public void setAction(String strAction) throws ApplicationException { 179 strAction=strAction.toLowerCase(); 180 if(strAction.equals("move") || strAction.equals("rename")) action=ACTION_MOVE; 181 else if(strAction.equals("copy")) action=ACTION_COPY; 182 else if(strAction.equals("delete")) action=ACTION_DELETE; 183 else if(strAction.equals("read")) action=ACTION_READ; 184 else if(strAction.equals("readbinary")) action=ACTION_READ_BINARY; 185 else if(strAction.equals("write")) action=ACTION_WRITE; 186 else if(strAction.equals("append")) action=ACTION_APPEND; 187 else if(strAction.equals("upload")) action=ACTION_UPLOAD; 188 else if(strAction.equals("uploadall")) action=ACTION_UPLOAD_ALL; 189 else if(strAction.equals("info")) action=ACTION_INFO; 190 else if(strAction.equals("touch")) action=ACTION_TOUCH; 191 else 192 throw new ApplicationException("invalid value ["+strAction+"] for attribute action","values for attribute action are:info,move,rename,copy,delete,read,readbinary,write,append,upload,uploadall,touch"); 193 } 194 195 /** set the value destination 196 * Absolute pathname of directory or file on web server. 197 * @param destination value to set 198 **/ 199 public void setDestination(String destination) { 200 this.strDestination=destination;//ResourceUtil.toResourceNotExisting(pageContext ,destination); 201 } 202 203 /** set the value output 204 * Content of the file to be created. 205 * @param output value to set 206 **/ 207 public void setOutput(Object output) { 208 if(output==null)this.output=""; 209 else this.output=output; 210 } 211 212 /** set the value file 213 * Absolute pathname of file on web server. 214 * @param file value to set 215 **/ 216 public void setFile(String file) { 217 this.file=ResourceUtil.toResourceNotExisting(pageContext ,file); 218 219 } 220 221 /** set the value mode 222 * Applies only to Solaris and HP-UX. Permissions. Octal values of UNIX chmod command. Assigned to owner, group, and other, respectively. 223 * @param mode value to set 224 * @throws PageException 225 **/ 226 public void setMode(String mode) throws PageException { 227 this.mode=toMode(mode); 228 } 229 230 231 public static int toMode(String mode) throws PageException { 232 if(StringUtil.isEmpty(mode,true)) return -1; 233 try { 234 return ModeUtil.toOctalMode(mode); 235 } 236 catch (IOException e) { 237 throw Caster.toPageException(e); 238 } 239 } 240 241 242 /** set the value variable 243 * Name of variable to contain contents of text file. 244 * @param variable value to set 245 **/ 246 public void setVariable(String variable) { 247 this.variable=variable; 248 } 249 250 /** set the value filefield 251 * Name of form field used to select the file. 252 * @param filefield value to set 253 **/ 254 public void setFilefield(String filefield) { 255 this.filefield=filefield; 256 } 257 258 /** set the value charset 259 * Character set name for the file contents. 260 * @param charset value to set 261 **/ 262 public void setCharset(String charset) { 263 if(StringUtil.isEmpty(charset)) return; 264 this.charset=CharsetUtil.toCharset(charset.trim()); 265 } 266 267 /** set the value acl 268 * used only for s3 resources, for all others ignored 269 * @param acl value to set 270 * @throws ApplicationException 271 * @Deprecated only exists for backward compatibility to old ra files. 272 **/ 273 public void setAcl(String acl) throws ApplicationException { 274 this.acl=acl; 275 } 276 public void setAcl(Object acl) { 277 this.acl=acl; 278 } 279 public void setStoreacl(Object acl) { 280 this.acl=acl; 281 } 282 283 284 285 public void setServerpassword(String serverPassword) { 286 this.serverPassword=serverPassword; 287 } 288 289 /** set the value addnewline 290 * Yes: appends newline character to text written to file 291 * @param addnewline value to set 292 **/ 293 public void setAddnewline(boolean addnewline) { 294 this.addnewline=addnewline; 295 } 296 297 /** set the value attributes 298 * One attribute (Windows) or a comma-delimited list of attributes (other platforms) to set on the file. 299 * If omitted, the file's attributes are maintained. 300 * @param attributes value to set 301 **/ 302 public void setAttributes(String attributes) { 303 this.attributes=attributes; 304 } 305 306 /** set the value source 307 * Absolute pathname of file on web server. 308 * On Windows, use backward slashes; on UNIX, use forward slashes. 309 * @param source value to set 310 **/ 311 public void setSource(String source) { 312 this.source=ResourceUtil.toResourceNotExisting(pageContext ,source); 313 } 314 315 /** set the value nameconflict 316 * Action to take if filename is the same as that of a file in the directory. 317 * @param nameconflict value to set 318 * @throws ApplicationException 319 **/ 320 public void setNameconflict(String nameconflict) throws ApplicationException { 321 322 this.nameconflict = FileUtil.toNameConflict( nameconflict ); 323 } 324 325 326 /** set the value accept 327 * Limits the MIME types to accept. Comma-delimited list. For example, to permit JPG and Microsoft Word file uploads: 328 * accept = "image/jpg, application/msword" 329 * The browser uses file extension to determine file type. 330 * @param accept value to set 331 **/ 332 public void setAccept(String accept) { 333 this.accept=accept; 334 } 335 336 public void setStrict(boolean strict) { 337 this.strict=strict; 338 } 339 public void setCreatepath(boolean createPath) { 340 this.createPath=createPath; 341 } 342 343 /** 344 * @param result The result to set. 345 */ 346 public void setResult(String result) { 347 this.result = result; 348 } 349 350 351 @Override 352 public int doStartTag() throws PageException { 353 if(charset==null) charset=((PageContextImpl)pageContext).getResourceCharset(); 354 355 securityManager = pageContext.getConfig().getSecurityManager(); 356 357 switch(action){ 358 case ACTION_MOVE: actionMove(pageContext, securityManager,source, strDestination, nameconflict,serverPassword,acl, mode, attributes); 359 break; 360 case ACTION_COPY: actionCopy(pageContext, securityManager,source, strDestination, nameconflict,serverPassword,acl, mode, attributes); 361 break; 362 case ACTION_DELETE: actionDelete(); 363 break; 364 case ACTION_READ: actionRead(); 365 break; 366 case ACTION_READ_BINARY: actionReadBinary(); 367 break; 368 case ACTION_UPLOAD: actionUpload(); 369 break; 370 case ACTION_UPLOAD_ALL: actionUploadAll(); 371 break; 372 case ACTION_INFO: actionInfo(); 373 break; 374 case ACTION_TOUCH: actionTouch(); 375 break; 376 case ACTION_UNDEFINED: throw new ApplicationException("missing attribute action"); // should never happens 377 378 // write and append 379 default: 380 return EVAL_BODY_BUFFERED; 381 } 382 return SKIP_BODY; 383 } 384 385 @Override 386 public int doAfterBody() throws ApplicationException { 387 if(action==ACTION_APPEND || action==ACTION_WRITE) { 388 String body = bodyContent.getString(); 389 if(!StringUtil.isEmpty(body)){ 390 if(!StringUtil.isEmpty(output)) 391 throw new ApplicationException("if a body is defined for the tag, the attribute [output] is not allowed"); 392 output=body; 393 } 394 } 395 return SKIP_BODY; 396 } 397 398 @Override 399 public int doEndTag() throws PageException { 400 switch(action){ 401 case ACTION_APPEND: actionAppend(); 402 break; 403 case ACTION_WRITE: actionWrite(); 404 break; 405 } 406 407 return EVAL_PAGE; 408 } 409 410 public void hasBody(boolean hasBody) { 411 if(output==null && hasBody) output=""; 412 } 413 414 /** 415 * move source file to destination path or file 416 * @throws PageException 417 */ 418 public static void actionMove(PageContext pageContext, lucee.runtime.security.SecurityManager securityManager, 419 Resource source, String strDestination, int nameconflict,String serverPassword, 420 Object acl, int mode, String attributes) throws PageException { 421 if(nameconflict==NAMECONFLICT_UNDEFINED) nameconflict=NAMECONFLICT_OVERWRITE; 422 423 if(source==null) 424 throw new ApplicationException("attribute source is not defined for tag file"); 425 if(StringUtil.isEmpty(strDestination)) 426 throw new ApplicationException("attribute destination is not defined for tag file"); 427 428 Resource destination=toDestination(pageContext,strDestination,source); 429 430 securityManager.checkFileLocation(pageContext.getConfig(),source,serverPassword); 431 securityManager.checkFileLocation(pageContext.getConfig(),destination,serverPassword); 432 if(source.equals(destination)) return ; 433 434 // source 435 if(!source.exists()) 436 throw new ApplicationException("source file ["+source.toString()+"] doesn't exist"); 437 else if(!source.isFile()) 438 throw new ApplicationException("source file ["+source.toString()+"] is not a file"); 439 else if(!source.isReadable() || !source.isWriteable()) 440 throw new ApplicationException("no access to source file ["+source.toString()+"]"); 441 442 // destination 443 if(destination.isDirectory()) destination=destination.getRealResource(source.getName()); 444 if(destination.exists()) { 445 // SKIP 446 if(nameconflict==NAMECONFLICT_SKIP) return; 447 // OVERWRITE 448 else if(nameconflict==NAMECONFLICT_OVERWRITE) destination.delete(); 449 // MAKEUNIQUE 450 else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) destination=makeUnique(destination); 451 // ERROR 452 else throw new ApplicationException("destiniation file ["+destination.toString()+"] already exist"); 453 } 454 455 456 try { 457 source.moveTo(destination); 458 459 } 460 catch(Throwable t) { 461 ExceptionUtil.rethrowIfNecessary(t); 462 t.printStackTrace(); 463 throw new ApplicationException(t.getMessage()); 464 } 465 setACL(destination,acl); 466 setMode(destination,mode); 467 setAttributes(destination,attributes); 468 } 469 470 private static Resource toDestination(PageContext pageContext,String path, Resource source) { 471 if(source!=null && path.indexOf(File.separatorChar)==-1 && path.indexOf('/')==-1 && path.indexOf('\\')==-1) { 472 Resource p = source.getParentResource(); 473 if(p!=null)return p.getRealResource(path); 474 } 475 return ResourceUtil.toResourceNotExisting(pageContext ,path); 476 } 477 478 /** 479 * copy source file to destination file or path 480 * @throws PageException 481 */ 482 public static void actionCopy(PageContext pageContext, lucee.runtime.security.SecurityManager securityManager, 483 Resource source, String strDestination, int nameconflict,String serverPassword, 484 Object acl, int mode, String attributes) throws PageException { 485 if(nameconflict==NAMECONFLICT_UNDEFINED) nameconflict=NAMECONFLICT_OVERWRITE; 486 487 if(source==null) 488 throw new ApplicationException("attribute source is not defined for tag file"); 489 if(StringUtil.isEmpty(strDestination)) 490 throw new ApplicationException("attribute destination is not defined for tag file"); 491 492 Resource destination=toDestination(pageContext,strDestination,source); 493 494 495 securityManager.checkFileLocation(pageContext.getConfig(),source,serverPassword); 496 securityManager.checkFileLocation(pageContext.getConfig(),destination,serverPassword); 497 498 // source 499 if(!source.exists()) 500 throw new ApplicationException("source file ["+source.toString()+"] doesn't exist"); 501 else if(!source.isFile()) 502 throw new ApplicationException("source file ["+source.toString()+"] is not a file"); 503 else if(!source.canRead()) 504 throw new ApplicationException("no access to source file ["+source.toString()+"]"); 505 506 // destination 507 if(destination.isDirectory()) destination=destination.getRealResource(source.getName()); 508 if(destination.exists()) { 509 // SKIP 510 if(nameconflict==NAMECONFLICT_SKIP) return; 511 // SKIP 512 else if(nameconflict==NAMECONFLICT_OVERWRITE) destination.delete(); 513 // MAKEUNIQUE 514 else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) destination=makeUnique(destination); 515 // ERROR 516 else throw new ApplicationException("destiniation file ["+destination.toString()+"] already exist"); 517 } 518 519 try { 520 IOUtil.copy(source,destination); 521 } 522 catch(IOException e) { 523 524 ApplicationException ae = new ApplicationException("can't copy file ["+source+"] to ["+destination+"]",e.getMessage()); 525 ae.setStackTrace(e.getStackTrace()); 526 throw ae; 527 } 528 setACL(destination,acl); 529 setMode(destination,mode); 530 setAttributes(destination,attributes); 531 } 532 533 private static void setACL(Resource res,Object acl) throws PageException { 534 String scheme = res.getResourceProvider().getScheme(); 535 536 if("s3".equalsIgnoreCase(scheme)){ 537 S3Resource s3r=(S3Resource) res; 538 539 if(acl!=null){ 540 try { 541 // old way 542 if(Decision.isString(acl)) { 543 s3r.setACL(S3.toIntACL(Caster.toString(acl))); 544 } 545 // new way 546 else { 547 StoreSetACL.invoke(s3r, acl); 548 } 549 } catch (IOException e) { 550 throw Caster.toPageException(e); 551 } 552 } 553 554 } 555 // set acl for s3 resource 556 /*if(res instanceof S3Resource) { 557 ((S3Resource)res).setACL(acl); 558 }*/ 559 } 560 561 private static Resource makeUnique(Resource res) { 562 563 String ext=getFileExtension(res); 564 String name=getFileName(res); 565 ext=(ext==null)?"":"."+ext; 566 int count=0; 567 while(res.exists()) { 568 res=res.getParentResource().getRealResource(name+(++count)+ext); 569 } 570 571 return res; 572 } 573 574 /** 575 * copy source file to destination file or path 576 * @throws PageException 577 */ 578 private void actionDelete() throws PageException { 579 checkFile(false,false,false,false); 580 setACL(file,acl); 581 try { 582 if(!file.delete()) throw new ApplicationException("can't delete file ["+file+"]"); 583 } 584 catch(Throwable t) { 585 ExceptionUtil.rethrowIfNecessary(t); 586 throw new ApplicationException(t.getMessage()); 587 } 588 } 589 590 /** 591 * read source file 592 * @throws PageException 593 */ 594 private void actionRead() throws PageException { 595 if(variable==null) 596 throw new ApplicationException("attribute variable is not defined for tag file"); 597 checkFile(false,false,true,false); 598 //print.ln(charset); 599 //TextFile tf=new TextFile(file.getAbsolutePath()); 600 601 try { 602 pageContext.setVariable(variable,IOUtil.toString(file,charset)); 603 } 604 catch (IOException e) { 605 606 throw new ApplicationException("can't read file ["+file+"]",e.getMessage()); 607 } 608 609 } 610 611 /** 612 * read source file 613 * @throws PageException 614 */ 615 private void actionReadBinary() throws PageException { 616 if(variable==null) 617 throw new ApplicationException("attribute variable is not defined for tag file"); 618 checkFile(false,false,true,false); 619 620 //TextFile tf=new TextFile(file.getAbsolutePath()); 621 622 try { 623 pageContext.setVariable(variable,IOUtil.toBytes(file)); 624 }catch (IOException e) { 625 throw new ApplicationException("can't read binary file ["+source.toString()+"]",e.getMessage()); 626 } 627 } 628 629 /** 630 * write to the source file 631 * @throws PageException 632 */ 633 private void actionWrite() throws PageException { 634 if(output==null) 635 throw new ApplicationException("attribute output is not defined for tag file"); 636 checkFile(createPath,true,false,true); 637 638 try { 639 if(output instanceof InputStream) { 640 IOUtil.copy( 641 (InputStream)output, 642 file, 643 false); 644 } 645 else if(Decision.isCastableToBinary(output,false)) { 646 IOUtil.copy( 647 new ByteArrayInputStream(Caster.toBinary(output)), 648 file, 649 true); 650 } 651 else { 652 String content=Caster.toString(output); 653 if(fixnewline)content=doFixNewLine(content); 654 if(addnewline) content+=SystemUtil.getOSSpecificLineSeparator(); 655 656 IOUtil.write(file,content,charset,false); 657 658 } 659 } 660 catch (UnsupportedEncodingException e) { 661 throw new ApplicationException("Unsupported Charset Definition ["+charset+"]",e.getMessage()); 662 } 663 catch (IOException e) { 664 665 throw new ApplicationException("can't write file "+file.getAbsolutePath(),e.getMessage()); 666 } 667 setACL(file,acl); 668 setMode(file,mode); 669 setAttributes(file,attributes); 670 } 671 672 /** 673 * write to the source file 674 * @throws PageException 675 */ 676 private void actionTouch() throws PageException { 677 checkFile(createPath,true,true,true); 678 679 try { 680 ResourceUtil.touch(file); 681 } 682 catch (IOException e) { 683 684 throw new ApplicationException("can't touch file "+file.getAbsolutePath(),e.getMessage()); 685 } 686 687 setACL(file,acl); 688 setMode(file,mode); 689 setAttributes(file,attributes); 690 } 691 692 693 694 /** 695 * append data to source file 696 * @throws PageException 697 */ 698 private void actionAppend() throws PageException { 699 if(output==null) 700 throw new ApplicationException("attribute output is not defined for tag file"); 701 checkFile(createPath,true,false,true); 702 703 try { 704 705 if(!file.exists()) file.createNewFile(); 706 String content=Caster.toString(output); 707 if(fixnewline)content=doFixNewLine(content); 708 if(addnewline) content+=SystemUtil.getOSSpecificLineSeparator(); 709 IOUtil.write(file,content,charset,true); 710 711 } 712 catch (UnsupportedEncodingException e) { 713 throw new ApplicationException("Unsupported Charset Definition ["+charset+"]",e.getMessage()); 714 } 715 catch (IOException e) { 716 throw new ApplicationException("can't write file",e.getMessage()); 717 } 718 setACL(file,acl); 719 setMode(file,mode); 720 setAttributes(file,attributes); 721 } 722 723 private String doFixNewLine(String content) { 724 // TODO replace new line with system new line 725 return content; 726 } 727 728 /** 729 * list all files and directories inside a directory 730 * @throws PageException 731 */ 732 private void actionInfo() throws PageException { 733 734 if(variable==null) 735 throw new ApplicationException("attribute variable is not defined for tag file"); 736 checkFile(false,false,false,false); 737 738 Struct sct =new StructImpl(); 739 pageContext.setVariable(variable,sct); 740 741 // fill data to query 742 sct.setEL(KeyConstants._name,file.getName()); 743 sct.setEL(KeyConstants._size,Long.valueOf(file.length())); 744 sct.setEL(KeyConstants._type,file.isDirectory()?"Dir":"File"); 745 sct.setEL("dateLastModified",new DateTimeImpl(pageContext,file.lastModified(),false)); 746 sct.setEL("attributes",getFileAttribute(file)); 747 if(SystemUtil.isUnix())sct.setEL(KeyConstants._mode,new ModeObjectWrap(file)); 748 749 try { 750 BufferedImage bi = ImageUtil.toBufferedImage(file, null); 751 if(bi!=null) { 752 Struct img =new StructImpl(); 753 img.setEL(KeyConstants._width,new Double(bi.getWidth())); 754 img.setEL(KeyConstants._height,new Double(bi.getHeight())); 755 sct.setEL(KeyConstants._img,img); 756 } 757 } 758 catch (Throwable t) { 759 ExceptionUtil.rethrowIfNecessary(t); 760 } 761 } 762 763 private static String getFileAttribute(Resource file){ 764 return file.exists() && !file.canWrite() ? "R".concat(file.isHidden() ? "H" : "") : file.isHidden() ? "H" : ""; 765 } 766 767 /** 768 * read source file 769 * @throws PageException 770 */ 771 772 public void actionUpload() throws PageException { 773 FormItem item=getFormItem(pageContext,filefield); 774 Struct cffile = _actionUpload(pageContext,securityManager,item,strDestination,nameconflict,accept,strict,mode,attributes,acl,serverPassword); 775 if(StringUtil.isEmpty(result)) { 776 pageContext.undefinedScope().set(KeyConstants._file,cffile); 777 pageContext.undefinedScope().set("cffile",cffile); 778 } 779 else { 780 pageContext.setVariable(result,cffile); 781 } 782 } 783 784 785 public static Struct actionUpload(PageContext pageContext,lucee.runtime.security.SecurityManager securityManager,String filefield, 786 String strDestination,int nameconflict,String accept,boolean strict,int mode,String attributes,Object acl,String serverPassword) throws PageException { 787 FormItem item=getFormItem(pageContext,filefield); 788 return _actionUpload(pageContext,securityManager,item,strDestination,nameconflict,accept,strict,mode,attributes,acl,serverPassword); 789 } 790 791 public void actionUploadAll() throws PageException { 792 Array arr=actionUploadAll(pageContext,securityManager,strDestination,nameconflict,accept,strict,mode,attributes,acl,serverPassword); 793 if(StringUtil.isEmpty(result)) { 794 Struct sct; 795 if(arr!=null && arr.size()>0) sct=(Struct) arr.getE(1); 796 else sct=new StructImpl(); 797 798 pageContext.undefinedScope().set(KeyConstants._file,sct); 799 pageContext.undefinedScope().set("cffile",sct); 800 } 801 else { 802 pageContext.setVariable(result,arr); 803 } 804 } 805 806 807 public static Array actionUploadAll(PageContext pageContext,lucee.runtime.security.SecurityManager securityManager, 808 String strDestination,int nameconflict,String accept,boolean strict,int mode,String attributes,Object acl,String serverPassword) throws PageException { 809 FormItem[] items=getFormItems(pageContext); 810 Struct sct=null; 811 Array arr=new ArrayImpl(); 812 for(int i=0;i<items.length;i++){ 813 sct = _actionUpload(pageContext,securityManager,items[i],strDestination,nameconflict,accept,strict,mode,attributes,acl,serverPassword); 814 arr.appendEL(sct); 815 } 816 return arr; 817 } 818 819 private static synchronized Struct _actionUpload(PageContext pageContext, lucee.runtime.security.SecurityManager securityManager, 820 FormItem formItem,String strDestination,int nameconflict,String accept,boolean strict,int mode,String attributes,Object acl,String serverPassword) throws PageException { 821 if(nameconflict==NAMECONFLICT_UNDEFINED) nameconflict=NAMECONFLICT_ERROR; 822 823 boolean fileWasRenamed=false; 824 boolean fileWasAppended=false; 825 boolean fileExisted=false; 826 boolean fileWasOverwritten=false; 827 828 829 String contentType=formItem.getContentType(); 830 831 832 // set cffile struct 833 Struct cffile=new StructImpl(); 834 835 long length = formItem.getResource().length(); 836 cffile.set("timecreated",new DateTimeImpl(pageContext.getConfig())); 837 cffile.set("timelastmodified",new DateTimeImpl(pageContext.getConfig())); 838 cffile.set("datelastaccessed",new DateImpl(pageContext)); 839 cffile.set("oldfilesize",Long.valueOf(length)); 840 cffile.set("filesize",Long.valueOf(length)); 841 cffile.set("contenttype",ListFirst.call(pageContext,contentType,"/")); 842 cffile.set("contentsubtype",ListLast.call(pageContext,contentType,"/")); 843 844 // client file 845 String strClientFile=formItem.getName(); 846 while(strClientFile.indexOf('\\')!=-1) 847 strClientFile=strClientFile.replace('\\','/'); 848 Resource clientFile=pageContext.getConfig().getResource(strClientFile); 849 String clientFileName=clientFile.getName(); 850 851 // check file type 852 checkContentType(contentType,accept,getFileExtension(clientFile),strict); 853 854 855 //String dir=clientFile.getParent(); 856 //dir=correctDirectory(dir); 857 858 cffile.set("clientdirectory",getParent(clientFile)); 859 cffile.set("clientfile",clientFile.getName()); 860 cffile.set("clientfileext",getFileExtension(clientFile)); 861 cffile.set("clientfilename",getFileName(clientFile)); 862 863 // check destination 864 if(StringUtil.isEmpty(strDestination)) 865 throw new ApplicationException("attribute destination is not defined in tag file"); 866 867 868 Resource destination=toDestination(pageContext,strDestination,null); 869 870 securityManager.checkFileLocation(pageContext.getConfig(),destination,serverPassword); 871 872 if(destination.isDirectory()) 873 destination=destination.getRealResource(clientFileName); 874 else if(!destination.exists() && (strDestination.endsWith("/") || strDestination.endsWith("\\"))) 875 destination=destination.getRealResource(clientFileName); 876 else if(!clientFileName.equalsIgnoreCase(destination.getName())) { 877 if(ResourceUtil.getExtension(destination, null)==null) 878 destination=destination.getRealResource(clientFileName); 879 else 880 fileWasRenamed=true; 881 } 882 883 // check parent destination -> directory of the desinatrion 884 Resource parentDestination=destination.getParentResource(); 885 886 if(!parentDestination.exists()) { 887 Resource pp = parentDestination.getParentResource(); 888 if(pp==null || !pp.exists()) 889 throw new ApplicationException("attribute destination has an invalid value ["+destination+"], directory ["+parentDestination+"] doesn't exist"); 890 try { 891 parentDestination.createDirectory(true); 892 } 893 catch (IOException e) { 894 throw Caster.toPageException(e); 895 } 896 } 897 else if(!parentDestination.canWrite()) 898 throw new ApplicationException("can't write to destination directory ["+parentDestination+"], no access to write"); 899 900 // set server variables 901 cffile.set("serverdirectory",getParent(destination)); 902 cffile.set("serverfile",destination.getName()); 903 cffile.set("serverfileext",getFileExtension(destination)); 904 cffile.set("serverfilename",getFileName(destination)); 905 cffile.set("attemptedserverfile",destination.getName()); 906 907 908 // check nameconflict 909 if(destination.exists()) { 910 fileExisted=true; 911 if(nameconflict==NAMECONFLICT_ERROR) { 912 throw new ApplicationException("destination file ["+destination+"] already exist"); 913 } 914 else if(nameconflict==NAMECONFLICT_SKIP) { 915 cffile.set("fileexisted",Caster.toBoolean(fileExisted)); 916 cffile.set("filewasappended",Boolean.FALSE); 917 cffile.set("filewasoverwritten",Boolean.FALSE); 918 cffile.set("filewasrenamed",Boolean.FALSE); 919 cffile.set("filewassaved",Boolean.FALSE); 920 return cffile; 921 } 922 else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) { 923 destination=makeUnique(destination); 924 fileWasRenamed=true; 925 926 //if(fileWasRenamed) { 927 cffile.set("serverdirectory",getParent(destination)); 928 cffile.set("serverfile",destination.getName()); 929 cffile.set("serverfileext",getFileExtension(destination)); 930 cffile.set("serverfilename",getFileName(destination)); 931 cffile.set("attemptedserverfile",destination.getName()); 932 //} 933 } 934 else if(nameconflict==NAMECONFLICT_OVERWRITE) { 935 //fileWasAppended=true; 936 fileWasOverwritten=true; 937 if(!destination.delete()) 938 if(destination.exists()) // hier hatte ich concurrent problem das damit ausgeraeumt ist 939 throw new ApplicationException("can't delete destination file ["+destination+"]"); 940 } 941 // for "overwrite" no action is neded 942 943 } 944 945 try { 946 destination.createNewFile(); 947 IOUtil.copy(formItem.getResource(),destination); 948 } 949 catch(Throwable t) { 950 throw Caster.toPageException(t); 951 } 952 953 // Set cffile/file struct 954 955 cffile.set("fileexisted",Caster.toBoolean(fileExisted)); 956 cffile.set("filewasappended",Caster.toBoolean(fileWasAppended)); 957 cffile.set("filewasoverwritten",Caster.toBoolean(fileWasOverwritten)); 958 cffile.set("filewasrenamed",Caster.toBoolean(fileWasRenamed)); 959 cffile.set("filewassaved",Boolean.TRUE); 960 961 962 setACL(destination,acl); 963 setMode(destination,mode); 964 setAttributes(destination, attributes); 965 return cffile; 966 } 967 968 /** 969 * check if the content ii ok 970 * @param contentType 971 * @throws PageException 972 */ 973 private static void checkContentType(String contentType,String accept,String ext,boolean strict) throws PageException { 974 975 if(!StringUtil.isEmpty(ext,true)){ 976 ext=ext.trim().toLowerCase(); 977 if(ext.startsWith("*."))ext=ext.substring(2); 978 if(ext.startsWith("."))ext=ext.substring(1); 979 } 980 else ext=null; 981 982 if(StringUtil.isEmpty(accept,true)) return; 983 984 985 MimeType mt = MimeType.getInstance(contentType),sub; 986 987 Array whishedTypes=ListUtil.listToArrayRemoveEmpty(accept,','); 988 int len=whishedTypes.size(); 989 for(int i=1;i<=len;i++) { 990 String whishedType=Caster.toString(whishedTypes.getE(i)).trim().toLowerCase(); 991 if(whishedType.equals("*")) return; 992 // check mimetype 993 if(ListUtil.len(whishedType, "/", true)==2){ 994 sub=MimeType.getInstance(whishedType); 995 if(mt.match(sub)) return; 996 } 997 998 // check extension 999 if(ext!=null && !strict){ 1000 if(whishedType.startsWith("*."))whishedType=whishedType.substring(2); 1001 if(whishedType.startsWith("."))whishedType=whishedType.substring(1); 1002 if(ext.equals(whishedType)) return; 1003 } 1004 } 1005 throw new ApplicationException("The MIME type of the uploaded file ["+contentType+"] was not accepted by the server.","only this ["+accept+"] mime type are accepted"); 1006 } 1007 1008 /** 1009 * rreturn fileItem matching to filefiled definition or throw a exception 1010 * @return FileItem 1011 * @throws ApplicationException 1012 */ 1013 private static FormItem getFormItem(PageContext pageContext, String filefield) throws PageException { 1014 // check filefield 1015 if(StringUtil.isEmpty(filefield)){ 1016 FormItem[] items = getFormItems(pageContext); 1017 if(ArrayUtil.isEmpty(items)) 1018 throw new ApplicationException("no file send with this form"); 1019 return items[0]; 1020 } 1021 1022 PageException pe = pageContext.formScope().getInitException(); 1023 if(pe!=null) throw pe; 1024 lucee.runtime.type.scope.Form upload = pageContext.formScope(); 1025 FormItem fileItem = upload.getUploadResource(filefield); 1026 if(fileItem==null) { 1027 FormItem[] items = upload.getFileItems(); 1028 StringBuilder sb=new StringBuilder(); 1029 for(int i=0;i<items.length;i++){ 1030 if(i!=0) sb.append(", "); 1031 sb.append(items[i].getFieldName()); 1032 } 1033 String add="."; 1034 if(sb.length()>0) add=", valid field names are ["+sb+"]."; 1035 1036 1037 if(pageContext.formScope().get(filefield,null)==null) 1038 throw new ApplicationException("form field ["+filefield+"] is not a file field"+add); 1039 throw new ApplicationException("form field ["+filefield+"] doesn't exist or has no content"+add); 1040 } 1041 1042 return fileItem; 1043 } 1044 1045 private static FormItem[] getFormItems(PageContext pageContext) throws PageException { 1046 PageException pe = pageContext.formScope().getInitException(); 1047 if(pe!=null) throw pe; 1048 1049 Form scope = pageContext.formScope(); 1050 return scope.getFileItems(); 1051 } 1052 1053 1054 /** 1055 * get file extension of a file object 1056 * @param file file object 1057 * @return extnesion 1058 */ 1059 private static String getFileExtension(Resource file) { 1060 String name=file.getName(); 1061 String[] arr; 1062 try { 1063 arr = ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(name, '.')); 1064 } catch (PageException e) { 1065 arr=null; 1066 } 1067 if(arr.length<2) return ""; 1068 1069 return arr[arr.length-1]; 1070 } 1071 1072 /** 1073 * get file name of a file object without extension 1074 * @param file file object 1075 * @return name of the file 1076 */ 1077 private static String getFileName(Resource file) { 1078 String name=file.getName(); 1079 int pos=name.lastIndexOf("."); 1080 1081 if(pos==-1)return name; 1082 return name.substring(0,pos); 1083 } 1084 1085 /*private String correctDirectory(Resource resource) { 1086 if(StringUtil.isEmpty(resource,true)) return ""; 1087 resource=resource.trim(); 1088 if((StringUtil.endsWith(resource, '/') || StringUtil.endsWith(resource, '\\')) && resource.length()>1) { 1089 return resource.substring(0,resource.length()-1); 1090 } 1091 return resource; 1092 }*/ 1093 1094 private static String getParent(Resource res) { 1095 Resource parent = res.getParentResource(); 1096 //print.out("res:"+res); 1097 //print.out("parent:"+parent); 1098 if(parent==null) return ""; 1099 return ResourceUtil.getCanonicalPathEL(parent); 1100 } 1101 1102 1103 private void checkFile(boolean createParent, boolean create, boolean canRead, boolean canWrite) throws PageException { 1104 if(file==null) 1105 throw new ApplicationException("attribute file is not defined for tag file"); 1106 1107 securityManager.checkFileLocation(pageContext.getConfig(),file,serverPassword); 1108 if(!file.exists()) { 1109 if(create) { 1110 Resource parent=file.getParentResource(); 1111 if(parent!=null && !parent.exists()) { 1112 if(createParent) parent.mkdirs(); 1113 else throw new ApplicationException("parent directory for ["+file+"] doesn't exist"); 1114 } 1115 try { 1116 file.createFile(false); 1117 } catch (IOException e) { 1118 throw new ApplicationException("invalid file ["+file+"]",e.getMessage()); 1119 } 1120 } 1121 else if(!file.isFile()) 1122 throw new ApplicationException("source file ["+file.toString()+"] is not a file"); 1123 else 1124 throw new ApplicationException("source file ["+file.toString()+"] doesn't exist"); 1125 } 1126 else if(!file.isFile()) 1127 throw new ApplicationException("source file ["+file.toString()+"] is not a file"); 1128 else if(canRead &&!file.canRead()) 1129 throw new ApplicationException("no read access to source file ["+file.toString()+"]"); 1130 else if(canWrite && !file.canWrite()) 1131 throw new ApplicationException("no write access to source file ["+file.toString()+"]"); 1132 1133 } 1134 1135 /** 1136 * set attributes on file 1137 * @param file 1138 * @throws PageException 1139 */ 1140 private static void setAttributes(Resource file,String attributes) throws PageException { 1141 if(!SystemUtil.isWindows() || StringUtil.isEmpty(attributes)) return; 1142 try { 1143 ResourceUtil.setAttribute(file, attributes); 1144 } 1145 catch (IOException e) { 1146 throw new ApplicationException("can't change attributes of file "+file,e.getMessage()); 1147 } 1148 } 1149 1150 /** 1151 * change mode of given file 1152 * @param file 1153 * @throws ApplicationException 1154 */ 1155 private static void setMode(Resource file,int mode) throws ApplicationException { 1156 if(mode==-1 || SystemUtil.isWindows()) return; 1157 try { 1158 file.setMode(mode); 1159 //FileUtil.setMode(file,mode); 1160 } catch (IOException e) { 1161 throw new ApplicationException("can't change mode of file "+file,e.getMessage()); 1162 } 1163 } 1164 1165 /** 1166 * @param fixnewline the fixnewline to set 1167 */ 1168 public void setFixnewline(boolean fixnewline) { 1169 this.fixnewline = fixnewline; 1170 } 1171}