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