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.type.scope; 020 021import java.io.BufferedReader; 022import java.io.File; 023import java.io.InputStream; 024import java.io.UnsupportedEncodingException; 025import java.util.ArrayList; 026import java.util.Iterator; 027import java.util.Map; 028 029import javax.servlet.ServletInputStream; 030import javax.servlet.http.HttpServletRequest; 031 032import lucee.commons.collection.MapFactory; 033import lucee.commons.io.IOUtil; 034import lucee.commons.io.res.Resource; 035import lucee.commons.lang.ByteNameValuePair; 036import lucee.commons.lang.StringUtil; 037import lucee.commons.net.URLItem; 038import lucee.runtime.PageContext; 039import lucee.runtime.PageContextImpl; 040import lucee.runtime.config.ConfigImpl; 041import lucee.runtime.engine.ThreadLocalPageContext; 042import lucee.runtime.exp.PageException; 043import lucee.runtime.listener.ApplicationContext; 044import lucee.runtime.net.http.ServletInputStreamDummy; 045import lucee.runtime.op.Caster; 046import lucee.runtime.type.Array; 047import lucee.runtime.type.util.ArrayUtil; 048import lucee.runtime.type.util.KeyConstants; 049import lucee.runtime.type.util.ListUtil; 050 051import org.apache.commons.fileupload.FileItemFactory; 052import org.apache.commons.fileupload.FileItemIterator; 053import org.apache.commons.fileupload.FileItemStream; 054import org.apache.commons.fileupload.disk.DiskFileItem; 055import org.apache.commons.fileupload.disk.DiskFileItemFactory; 056import org.apache.commons.fileupload.servlet.ServletFileUpload; 057import org.apache.commons.fileupload.servlet.ServletRequestContext; 058 059 060/** 061 * Form Scope 062 */ 063public final class FormImpl extends ScopeSupport implements Form,ScriptProtected { 064 065 066 private byte EQL=61; 067 private byte NL=10; 068 private byte AMP=38; 069 070 071 private Map<String,Item> _fileItems=MapFactory.<String,Item>getConcurrentMap(); 072 private Exception initException=null; 073 074 private String encoding=null; 075 private int scriptProtected=ScriptProtected.UNDEFINED; 076 private static final URLItem[] empty=new URLItem[0]; 077 //private static final ResourceFilter FILTER = new ExtensionResourceFilter(".upload",false); 078 private URLItem[] raw=empty; 079 private static int count=1; 080 081 private static final int HEADER_TEXT_PLAIN=0; 082 private static final int HEADER_MULTIPART_FORM_DATA=1; 083 private static final int HEADER_APP_URL_ENC=2; 084 private int headerType=-1; 085 086 /** 087 * standart class Constructor 088 */ 089 public FormImpl() { 090 super(true,"form",SCOPE_FORM); 091 } 092 093 @Override 094 public String getEncoding() { 095 return encoding; 096 } 097 098 @Override 099 public void setEncoding(ApplicationContext ac,String encoding) throws UnsupportedEncodingException { 100 encoding=encoding.trim().toUpperCase(); 101 if(encoding.equals(this.encoding)) return; 102 this.encoding = encoding; 103 if(!isInitalized()) return; 104 fillDecoded(raw,encoding,isScriptProtected(),ac.getSameFieldAsArray(Scope.SCOPE_FORM)); 105 setFieldNames(); 106 } 107 108 @Override 109 public void initialize(PageContext pc) { 110 111 112 if(encoding==null)encoding=((PageContextImpl)pc).getWebCharset().name(); 113 114 if(scriptProtected==ScriptProtected.UNDEFINED) { 115 scriptProtected=((pc.getApplicationContext().getScriptProtect()&ApplicationContext.SCRIPT_PROTECT_FORM)>0)? 116 ScriptProtected.YES:ScriptProtected.NO; 117 } 118 super.initialize(pc); 119 120 String contentType=pc. getHttpServletRequest().getContentType(); 121 122 if(contentType==null) return; 123 contentType=StringUtil.toLowerCase(contentType); 124 if(contentType.startsWith("multipart/form-data")) { 125 headerType=HEADER_MULTIPART_FORM_DATA; 126 initializeMultiPart(pc,isScriptProtected()); 127 } 128 else if(contentType.startsWith("text/plain")) { 129 headerType=HEADER_TEXT_PLAIN; 130 initializeUrlEncodedOrTextPlain(pc,'\n',isScriptProtected()); 131 } 132 else if(contentType.startsWith("application/x-www-form-urlencoded")) { 133 headerType=HEADER_APP_URL_ENC; 134 initializeUrlEncodedOrTextPlain(pc,'&',isScriptProtected()); 135 } 136 setFieldNames(); 137 } 138 139 void setFieldNames() { 140 if(size()>0) { 141 setEL(KeyConstants._fieldnames,ListUtil.arrayToList(keys(), ",")); 142 } 143 } 144 145 146 private void initializeMultiPart(PageContext pc, boolean scriptProteced) { 147 // get temp directory 148 Resource tempDir = ((ConfigImpl)pc.getConfig()).getTempDirectory(); 149 Resource tempFile; 150 151 // Create a new file upload handler 152 final String encoding=getEncoding(); 153 FileItemFactory factory = tempDir instanceof File? 154 new DiskFileItemFactory(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD,(File)tempDir): 155 new DiskFileItemFactory(); 156 157 ServletFileUpload upload = new ServletFileUpload(factory); 158 upload.setHeaderEncoding(encoding); 159 //ServletRequestContext c = new ServletRequestContext(pc.getHttpServletRequest()); 160 161 162 HttpServletRequest req = pc.getHttpServletRequest(); 163 ServletRequestContext context = new ServletRequestContext(req) { 164 public String getCharacterEncoding() { 165 return encoding; 166 } 167 }; 168 169 // Parse the request 170 try { 171 FileItemIterator iter = upload.getItemIterator(context); 172 //byte[] value; 173 InputStream is; 174 ArrayList<URLItem> list=new ArrayList<URLItem>(); 175 String fileName; 176 while (iter.hasNext()) { 177 FileItemStream item = iter.next(); 178 179 is=IOUtil.toBufferedInputStream(item.openStream()); 180 if (item.getContentType()==null || StringUtil.isEmpty(item.getName())) { 181 list.add(new URLItem(item.getFieldName(),new String(IOUtil.toBytes(is),encoding),false)); 182 } 183 else { 184 fileName=getFileName(); 185 tempFile=tempDir.getRealResource(fileName); 186 _fileItems.put(fileName, 187 new Item(tempFile,item.getContentType(),item.getName(),item.getFieldName())); 188 String value=tempFile.toString(); 189 IOUtil.copy(is, tempFile,true); 190 list.add(new URLItem(item.getFieldName(),value,false)); 191 } 192 } 193 194 195 raw= list.toArray(new URLItem[list.size()]); 196 fillDecoded(raw,encoding,scriptProteced,pc.getApplicationContext().getSameFieldAsArray(SCOPE_FORM)); 197 } 198 catch (Exception e) { 199 200 //throw new PageRuntimeException(Caster.toPageException(e)); 201 fillDecodedEL(new URLItem[0],encoding,scriptProteced,pc.getApplicationContext().getSameFieldAsArray(SCOPE_FORM)); 202 initException=e; 203 } 204 } 205 206 private static String getFileName() { 207 return "tmp-"+(count++)+".upload"; 208 } 209 210 /*private void initializeMultiPart(PageContext pc, boolean scriptProteced) { 211 212 File tempDir=FileWrapper.toFile(pc.getConfig().getTempDirectory()); 213 214 // Create a factory for disk-based file items 215 DiskFileItemFactory factory = new DiskFileItemFactory(-1,tempDir); 216 217 // Create a new file upload handler 218 ServletFileUpload upload = new ServletFileUpload(factory); 219 220 upload.setHeaderEncoding(getEncoding()); 221 222 //FileUpload fileUpload=new FileUpload(new DiskFileItemFactory(0,tempDir)); 223 java.util.List list; 224 try { 225 list = upload.parseRequest(pc.getHttpServletRequest()); 226 raw=new ByteNameValuePair[list.size()]; 227 228 for(int i=0;i<raw.length;i++) { 229 DiskFileItem val=(DiskFileItem) list.get(i); 230 if(val.isFormField()) { 231 raw[i]=new ByteNameValuePair(getBytes(val.getFieldName()),val.get(),false); 232 } 233 else { 234 print.out("-------------------------------"); 235 print.out("fieldname:"+val.getFieldName()); 236 print.out("name:"+val.getName()); 237 print.out("formfield:"+val.isFormField()); 238 print.out("memory:"+val.isInMemory()); 239 print.out("exist:"+val.getStoreLocation().getCanonicalFile().exists()); 240 241 fileItems.put(val.getFieldName().toLowerCase(),val); 242 243 raw[i]=new ByteNameValuePair(getBytes(val.getFieldName()),val.getStoreLocation().getCanonicalFile().toString().getBytes(),false); 244 //raw.put(val.getFieldName(),val.getStoreLocation().getCanonicalFile().toString()); 245 } 246 } 247 fillDecoded(raw,encoding,scriptProteced); 248 } 249 catch (Exception e) { 250 251 //throw new PageRuntimeException(Caster.toPageException(e)); 252 fillDecodedEL(new ByteNameValuePair[0],encoding,scriptProteced); 253 initException=e; 254 } 255 }*/ 256 257 private void initializeUrlEncodedOrTextPlain(PageContext pc, char delimiter, boolean scriptProteced) { 258 BufferedReader reader=null; 259 try { 260 reader = pc.getHttpServletRequest().getReader(); 261 raw=setFrom___(IOUtil.toString(reader,false),delimiter); 262 fillDecoded(raw,encoding,scriptProteced,pc.getApplicationContext().getSameFieldAsArray(SCOPE_FORM)); 263 } 264 catch (Exception e) { 265 266 fillDecodedEL(new URLItem[0],encoding,scriptProteced,pc.getApplicationContext().getSameFieldAsArray(SCOPE_FORM)); 267 initException=e; 268 } 269 finally { 270 IOUtil.closeEL(reader); 271 } 272 } 273 274 275 @Override 276 public void release() { 277 release(ThreadLocalPageContext.get()); 278 } 279 280 @Override 281 public void release(PageContext pc) { 282 super.release(pc); 283 encoding=null; 284 scriptProtected=ScriptProtected.UNDEFINED; 285 raw=empty; 286 287 if(!_fileItems.isEmpty()) { 288 Iterator<Item> it = _fileItems.values().iterator(); 289 Item item; 290 while(it.hasNext()) { 291 item=it.next(); 292 item.getResource().delete(); 293 } 294 _fileItems.clear(); 295 } 296 initException=null; 297 298 } 299 300 @Override 301 public FormItem[] getFileItems() { 302 if(_fileItems==null || _fileItems.isEmpty()) return new FormImpl.Item[0]; 303 304 Iterator<Item> it = _fileItems.values().iterator(); 305 FormImpl.Item[] rtn=new FormImpl.Item[_fileItems.size()]; 306 int index=0; 307 while(it.hasNext()){ 308 rtn[index++]=it.next(); 309 } 310 return rtn; 311 } 312 313 314 public DiskFileItem getFileUpload(String key) { 315 return null; 316 } 317 318 public FormItem getUploadResource(String key) { 319 key=key.trim(); 320 String lcKey = StringUtil.toLowerCase(key); 321 322 if(_fileItems==null || _fileItems.isEmpty()) return null; 323 324 Iterator<Entry<String, Item>> it = _fileItems.entrySet().iterator(); 325 Entry<String, Item> entry; 326 Item item; 327 while(it.hasNext()) { 328 entry=it.next(); 329 item = entry.getValue(); 330 // x 331 if(item.getFieldName().equalsIgnoreCase(key)) return item; 332 333 // form.x 334 if(lcKey.startsWith("form.")) { 335 lcKey=lcKey.substring(5).trim(); 336 if(item.getFieldName().equalsIgnoreCase(lcKey))return item; 337 } 338 339 // form . x 340 try { 341 Array array = ListUtil.listToArray(lcKey, '.'); 342 if(array.size()>1 && array.getE(1).toString().trim().equals("form")) { 343 array.removeE(1); 344 lcKey=ListUtil.arrayToList(array, ".").trim(); 345 if(item.getFieldName().equalsIgnoreCase(lcKey))return item; 346 } 347 } 348 catch (PageException e) {} 349 350 // /file.tmp 351 if(item.getResource().getAbsolutePath().equalsIgnoreCase(key))return item; 352 if(entry.getKey().equalsIgnoreCase(key))return item; 353 } 354 return null; 355 } 356 357 @Override 358 public PageException getInitException() { 359 if(initException!=null) 360 return Caster.toPageException(initException); 361 return null; 362 } 363 364 @Override 365 public void setScriptProtecting(ApplicationContext ac,boolean scriptProtected) { 366 int _scriptProtected = scriptProtected?ScriptProtected.YES:ScriptProtected.NO; 367 if(isInitalized() && _scriptProtected!=this.scriptProtected) { 368 fillDecodedEL(raw,encoding,scriptProtected,ac.getSameFieldAsArray(SCOPE_FORM)); 369 setFieldNames(); 370 } 371 this.scriptProtected=_scriptProtected; 372 } 373 374 public void reinitialize(ApplicationContext ac) { 375 if(isInitalized()) { 376 377 if(scriptProtected==ScriptProtected.UNDEFINED) { 378 scriptProtected=((ac.getScriptProtect()&ApplicationContext.SCRIPT_PROTECT_FORM)>0)? 379 ScriptProtected.YES:ScriptProtected.NO; 380 } 381 fillDecodedEL(raw,encoding,isScriptProtected(),ac.getSameFieldAsArray(SCOPE_FORM)); 382 setFieldNames(); 383 } 384 } 385 386 @Override 387 public boolean isScriptProtected() { 388 return scriptProtected==ScriptProtected.YES ; 389 } 390 391 /** 392 * @return the raw 393 */ 394 public URLItem[] getRaw() { 395 return raw; 396 } 397 398 public void addRaw(ApplicationContext ac,URLItem[] raw) { 399 URLItem[] nr=new URLItem[this.raw.length+raw.length]; 400 for(int i=0;i<this.raw.length;i++) { 401 nr[i]=this.raw[i]; 402 } 403 for(int i=0;i<raw.length;i++) { 404 nr[this.raw.length+i]=raw[i]; 405 } 406 this.raw=nr; 407 408 if(!isInitalized()) return; 409 fillDecodedEL(this.raw,encoding,isScriptProtected(),ac.getSameFieldAsArray(SCOPE_FORM)); 410 setFieldNames(); 411 } 412 413 private class Item implements FormItem { 414 Resource resource; 415 String contentType; 416 String name; 417 private String fieldName; 418 419 public Item(Resource resource, String contentType,String name, String fieldName) { 420 this.fieldName = fieldName; 421 this.name = name; 422 this.resource = resource; 423 this.contentType = contentType; 424 } 425 /** 426 * @return the resource 427 */ 428 public Resource getResource() { 429 return resource; 430 } 431 /** 432 * @return the contentType 433 */ 434 public String getContentType() { 435 return contentType; 436 } 437 /** 438 * @return the name 439 */ 440 public String getName() { 441 return name; 442 } 443 /** 444 * @return the fieldName 445 */ 446 public String getFieldName() { 447 return fieldName; 448 } 449 } 450 451 /** 452 * @return return content as a http header input stream 453 */ 454 public ServletInputStream getInputStream() { 455 if(headerType==HEADER_APP_URL_ENC) { 456 return new ServletInputStreamDummy(toBarr(raw,AMP)); 457 } 458 else if(headerType==HEADER_TEXT_PLAIN) { 459 return new ServletInputStreamDummy(toBarr(raw,NL)); 460 } 461 /*else if(headerType==HEADER_MULTIPART_FORM_DATA) { 462 return new FormImplInputStream(this); 463 // TODO 464 }*/ 465 return new ServletInputStreamDummy(new byte[]{}); 466 } 467 468 private byte[] toBarr(URLItem[] items, byte del) { 469 470 ByteNameValuePair[] raw=new ByteNameValuePair[items.length]; 471 for(int i=0;i<raw.length;i++) { 472 try { 473 raw[i]=new ByteNameValuePair(items[i].getName().getBytes("iso-8859-1"),items[i].getValue().getBytes("iso-8859-1"),items[i].isUrlEncoded()); 474 } catch (UnsupportedEncodingException e) {} 475 } 476 477 int size=0; 478 if(!ArrayUtil.isEmpty(raw)){ 479 for(int i=0;i<raw.length;i++) { 480 size+=raw[i].getName().length; 481 size+=raw[i].getValue().length; 482 size+=2; 483 } 484 size--; 485 } 486 byte[] barr = new byte[size],bname,bvalue; 487 int count=0; 488 489 for(int i=0;i<raw.length;i++) { 490 bname=raw[i].getName(); 491 bvalue=raw[i].getValue(); 492 // name 493 for(int y=0;y<bname.length;y++) { 494 barr[count++]=bname[y]; 495 } 496 barr[count++]=EQL; 497 // value 498 for(int y=0;y<bvalue.length;y++) { 499 barr[count++]=bvalue[y]; 500 } 501 if(i+1<raw.length)barr[count++]=del; 502 } 503 return barr; 504 } 505 506}