1 14 package org.wings.session; 15 16 import org.apache.commons.logging.Log; 17 import org.apache.commons.logging.LogFactory; 18 import org.wings.UploadFilterManager; 19 import org.wings.util.LocaleCharSet; 20 21 import javax.servlet.ServletInputStream ; 22 import javax.servlet.http.HttpServletRequest ; 23 import javax.servlet.http.HttpServletRequestWrapper ; 24 import java.io.*; 25 import java.net.URLEncoder ; 26 import java.util.*; 27 28 53 public class MultipartRequest 54 extends HttpServletRequestWrapper { 55 private final transient static Log log = LogFactory.getLog(MultipartRequest.class); 56 57 private static final int DEFAULT_MAX_POST_SIZE = 1024 * 1024; 59 private int maxSize; 60 private boolean urlencodedRequest; 61 62 private final HashMap parameters = new HashMap(); private final HashMap files = new HashMap(); private HashMap map; 65 66 71 public MultipartRequest(HttpServletRequest request) throws IOException { 72 this(request, DEFAULT_MAX_POST_SIZE); 73 } 74 75 81 public MultipartRequest(HttpServletRequest request, 82 int maxPostSize) throws IOException { 83 super(request); 84 85 if (request == null) 86 throw new IllegalArgumentException ("request cannot be null"); 87 if (maxPostSize <= 0) 88 throw new IllegalArgumentException ("maxPostSize must be positive"); 89 90 maxSize = maxPostSize; 91 92 processRequest(request); 93 } 94 95 101 public Enumeration getParameterNames() { 102 if (urlencodedRequest) return super.getParameterNames(); 103 104 final Iterator iter = parameters.keySet().iterator(); 105 return new Enumeration() { 106 public boolean hasMoreElements() { 107 return iter.hasNext(); 108 } 109 110 public Object nextElement() { 111 return iter.next(); 112 } 113 114 }; 115 } 116 117 125 public Iterator getFileNames() { 126 return files.keySet().iterator(); 127 } 128 129 public String [] getParameterValues(String name) { 130 if (urlencodedRequest) 131 return super.getParameterValues(name); 132 ArrayList v = (ArrayList) parameters.get(name); 133 if (v == null) return null; 134 String result[] = new String [v.size()]; 135 return (String []) v.toArray(result); 136 } 137 138 public Map getParameterMap() { 139 if (urlencodedRequest) 140 return super.getParameterMap(); 141 if (map == null) { 142 map = new HashMap(); 143 for (Iterator iterator = parameters.entrySet().iterator(); iterator.hasNext();) { 144 Map.Entry entry = (Map.Entry) iterator.next(); 145 List list = (List) entry.getValue(); 146 String [] values = (String []) list.toArray(new String [list.size()]); 147 map.put(entry.getKey(), values); 148 } 149 } 150 return map; 151 } 152 153 162 public String getFileName(String name) { 163 try { 164 UploadedFile file = (UploadedFile) files.get(name); 165 return file.getFileName(); } catch (Exception e) { 167 return null; 168 } 169 } 170 171 179 public String getFileId(String name) { 180 try { 181 UploadedFile file = (UploadedFile) files.get(name); 182 return file.getId(); } catch (Exception e) { 184 return null; 185 } 186 } 187 188 195 public String getContentType(String name) { 196 try { 197 UploadedFile file = (UploadedFile) files.get(name); 198 return file.getContentType(); } catch (Exception e) { 200 return null; 201 } 202 } 203 204 211 public File getFile(String name) { 212 try { 213 UploadedFile file = (UploadedFile) files.get(name); 214 return file.getFile(); } catch (Exception e) { 216 return null; 217 } 218 } 219 220 223 public final boolean isMultipart() { 224 return !urlencodedRequest; 225 } 226 227 230 protected void setException(String param, Exception ex) { 231 parameters.clear(); 232 files.clear(); 233 234 putParameter(param, "exception"); 235 putParameter(param, ex.getMessage()); 236 } 237 238 243 protected void processRequest(HttpServletRequest req) 244 throws IOException { 245 String type = req.getContentType(); 246 if (type == null || !type.toLowerCase().startsWith("multipart/form-data")) { 247 urlencodedRequest = true; 248 return; 249 } 250 urlencodedRequest = false; 251 252 for (Iterator iterator = req.getParameterMap().entrySet().iterator(); iterator.hasNext();) { 253 Map.Entry entry = (Map.Entry) iterator.next(); 254 parameters.put(entry.getKey(), new ArrayList(Arrays.asList((String []) entry.getValue()))); 255 } 256 257 String boundaryToken = extractBoundaryToken(type); 258 if (boundaryToken == null) { 259 270 throw new IOException("Separation boundary was not specified (BUG in Tomcat 3.* with Opera?)"); 271 } 272 273 MultipartInputStream mimeStream = null; 274 275 276 StringBuffer header = new StringBuffer (); 277 StringBuffer content = new StringBuffer (); 278 HashMap headers = null; 279 int currentByte = 0; 280 int currentPos = 0; 281 int currentTransformByte = 0; 282 String currentParam = null; 283 File uploadFile = null; 284 OutputStream fileStream = null; 285 boolean done; 286 int last = -1; 287 288 try { 289 mimeStream = new MultipartInputStream(req.getInputStream(), req.getContentLength(), maxSize); 290 while (currentByte != -1) { 291 done = false; 293 while ((currentByte = mimeStream.read()) != -1 && !done) { 294 header.append((char) currentByte); done = (last == '\n' && currentByte == '\r'); 296 last = currentByte; 297 } 298 if (currentByte == -1) 299 break; 300 301 headers = parseHeader(header.toString()); 302 header.setLength(0); 303 304 currentParam = (String ) headers.get("name"); 305 306 if (headers.size() == 1) { byte[] bytes = new byte[req.getContentLength()]; 308 currentPos = 0; 309 while ((currentByte = mimeStream.read()) != -1) { 310 bytes[currentPos] = (byte) currentByte; 311 currentPos++; 312 if (currentPos >= boundaryToken.length()) { 313 int i; 314 for (i = 0; i < boundaryToken.length(); i++) { 315 if (boundaryToken.charAt(boundaryToken.length() - i - 1) != bytes[currentPos - i - 1]) { 316 i = 0; 317 break; 318 } 319 } 320 if (i == boundaryToken.length()) { ByteArrayInputStream bais = new ByteArrayInputStream(bytes, 0, currentPos - boundaryToken.length() - 4); 322 InputStreamReader ir; 323 if (req.getCharacterEncoding() != null) 324 ir = new InputStreamReader(bais, req.getCharacterEncoding()); 328 else 329 ir = new InputStreamReader(bais); 330 content.setLength(0); 331 while ((currentTransformByte = ir.read()) != -1) { 332 content.append((char) currentTransformByte); 333 } 334 335 putParameter(currentParam, content.toString()); 336 break; 337 } 338 } 339 } 340 } else { String filename = (String ) headers.get("filename"); 342 if (filename != null && filename.length() != 0) { 343 int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\')); 345 if (slash > -1) { 346 filename = filename.substring(slash + 1); 347 } 348 String name = (String ) headers.get("name"); 349 350 String contentType = (String ) headers.get("content-type"); 351 try { 352 uploadFile = File.createTempFile("wings_uploaded", 353 "tmp"); 354 } catch (IOException e) { 355 log.error("couldn't create temp file in '" 356 + System.getProperty("java.io.tmpdir") 357 + "' (CATALINA_TMPDIR set correctly?)", 358 e); 359 throw e; 360 } 361 362 UploadedFile upload = new UploadedFile(filename, 363 contentType, 364 uploadFile); 365 fileStream = new FileOutputStream(uploadFile); 366 367 fileStream = UploadFilterManager.createFilterInstance(name, fileStream); 368 369 AccessibleByteArrayOutputStream byteArray = new AccessibleByteArrayOutputStream(); 370 byte[] bytes = null; 371 372 int blength = boundaryToken.length(); 373 int i; 374 while ((currentByte = mimeStream.read()) != -1) { 375 byteArray.write(currentByte); 376 for (i = 0; i < blength; i++) { 377 if (boundaryToken.charAt(blength - i - 1) != byteArray.charAt(-i - 1)) { 378 i = 0; 379 if (byteArray.size() > 512 + blength + 2) 380 byteArray.writeTo(fileStream, 512); 381 break; 382 } 383 } 384 if (i == blength) break; 386 } 387 bytes = byteArray.toByteArray(); 388 fileStream.write(bytes, 0, bytes.length - blength - 4); 389 fileStream.close(); 390 391 files.put(name, upload); 392 putParameter(name, upload.toString()); 393 } else { int i; 395 int blength = boundaryToken.length(); 396 while ((currentByte = mimeStream.read()) != -1) { 397 content.append((char) currentByte); 398 if (content.length() >= blength) { 399 for (i = 0; i < blength; i++) { 400 if (boundaryToken.charAt(blength - i - 1) != content.charAt(content.length() - i - 1)) { 401 i = 0; 402 break; 403 } 404 } 405 if (i == blength) 406 break; 407 } 408 } 409 } 410 } 411 412 currentByte = mimeStream.read(); 413 if (currentByte == '\r' && mimeStream.read() != '\n') 414 log.error("No line return char? " + currentByte); 415 if (currentByte == '-' && mimeStream.read() != '-') 416 log.error("?? No clue " + currentByte); 417 } 418 } catch (IOException ex) { 419 log.warn("upload", ex); 421 if (uploadFile != null) uploadFile.delete(); 422 setException(currentParam, ex); 423 } finally { 424 try { fileStream.close(); } catch (Exception ign) {} 425 try { mimeStream.close(); } catch (Exception ign) {} 426 } 427 } 428 429 private static class AccessibleByteArrayOutputStream extends ByteArrayOutputStream { 430 public byte charAt(int index) { 431 if (count + index < 0) { 432 log.warn("count: " + count + ", index: " + index + ", buffer: " + new String (buf)); 433 return -1; 434 } 435 if (index < 0) 436 return buf[count + index]; 437 if (index < count) 438 return buf[index]; 439 return -1; 440 } 441 442 public byte[] getBuffer() { 443 return buf; 444 } 445 446 public void writeTo(OutputStream out, int num) 447 throws IOException { 448 out.write(buf, 0, num); 449 System.arraycopy(buf, num, buf, 0, count - num); 450 count = count - num; 451 } 452 } 453 454 private HashMap parseHeader(String header) { 455 int lastHeader = -1; 456 String [] headerLines; 457 HashMap nameValuePairs = new HashMap(); 458 459 StringTokenizer stLines = new StringTokenizer(header, "\r\n", false); 460 headerLines = new String [stLines.countTokens()]; 461 462 while (stLines.hasMoreTokens()) { 464 String hLine = stLines.nextToken(); 465 if (hLine.length() == 0) continue; 466 470 if (lastHeader >= 0 && Character.isWhitespace(hLine.charAt(0))) 471 headerLines[lastHeader] += hLine; 472 else 473 headerLines[++lastHeader] = hLine; 474 } 475 476 for (int i = 0; i <= lastHeader; ++i) { 477 String currentHeader = headerLines[i]; 478 if (currentHeader.startsWith("Content-Type")) { 479 String contentType = currentHeader 480 .substring(currentHeader.indexOf(':') + 1); 481 int semiColonPos = contentType.indexOf(';'); 482 if (semiColonPos != -1) 483 contentType = contentType.substring(0, semiColonPos); 484 nameValuePairs.put("content-type", contentType.trim()); 485 continue; 486 } 487 488 if (!currentHeader.startsWith("Content-Disposition")) 489 continue; 490 491 StringTokenizer stTokens = new StringTokenizer(currentHeader, ";", false); 492 493 if (stTokens.countTokens() > 1) { 495 stTokens.nextToken(); StringTokenizer stnameValue = new StringTokenizer(stTokens.nextToken(), "=", false); 497 nameValuePairs.put(stnameValue.nextToken().trim(), trim(stnameValue.nextToken(), "\"")); 498 499 if (stTokens.hasMoreTokens()) { 501 stnameValue = new StringTokenizer(stTokens.nextToken(), "=", false); 502 503 String formType = stnameValue.nextToken().trim(); String filePath = trim(stnameValue.nextToken(), "\""); if (filePath.indexOf(":") != -1) 507 filePath = filePath.substring((filePath.indexOf(":") + 1)); 508 509 filePath = filePath.substring(filePath.lastIndexOf(File.separator) + 1); 511 nameValuePairs.put(formType, filePath); 512 } 513 } 514 } 515 return nameValuePairs; 516 } 517 518 521 private String trim(String source, String trimChar) { 522 String target = ""; 523 source = source.trim(); 525 526 if (source.indexOf(trimChar) != -1 && (source.lastIndexOf(trimChar) >= (source.indexOf(trimChar) + 1))) 528 target = source.substring(source.indexOf(trimChar) + 1, source.lastIndexOf(trimChar)); 530 531 return target; 532 } 533 534 private static class MultipartInputStream extends InputStream { 535 ServletInputStream istream = null; 536 int len, pos, maxLength; 537 538 public MultipartInputStream(ServletInputStream istream, int len, int maxLength) { 539 this.istream = istream; 540 this.len = len; 541 this.pos = 0; 542 this.maxLength = maxLength; 543 } 544 545 548 public int available() throws IOException { 549 return len - pos - 1; 550 } 551 552 556 public int read() throws IOException { 557 if (pos >= maxLength) 558 throw new IOException("Size (" + len + ") exceeds maxlength " + maxLength); 559 560 if (pos >= len) 561 return -1; 562 pos++; 563 564 return istream.read(); 565 } 566 567 public int read(byte b[]) throws IOException { 568 return read(b, 0, b.length); 569 } 570 571 public int read(byte b[], int off, int num) throws IOException { 572 if (off > 0) 573 istream.skip(off); 574 575 if (pos >= len) 576 return -1; 577 578 if (num > len - pos) 579 num = len - pos; 580 581 num = istream.read(b, 0, num); 582 pos += num; 583 584 if (pos >= maxLength) 585 throw new IOException("Size (" + len + ") exceeds maxlength " + maxLength); 586 587 return num; 588 } 589 590 public long skip(long num) throws IOException { 591 if (pos >= len) 592 return -1; 593 594 if (num > len - pos) 595 num = len - pos; 596 597 num = istream.skip(num); 598 pos += num; 599 600 if (pos >= maxLength) 601 throw new IOException("Size (" + len + ") exceeds maxlength " + maxLength); 602 603 return num; 604 } 605 606 public void close() throws IOException { 607 } 609 } 610 611 614 protected void putParameter(String name, String value) { 615 ArrayList v = (ArrayList) parameters.get(name); 616 if (v == null) { 618 v = new ArrayList(2); 619 parameters.put(name, v); 620 } 621 v.add(value); 622 } 623 624 627 private String extractBoundaryToken(String line) { 628 int index = line.indexOf("boundary="); 629 if (index == -1) { 630 return null; 631 } 632 String boundary = line.substring(index + 9); 636 return boundary; 637 } 638 639 644 private String extractContentType(String line) throws IOException { 645 String contentType = null; 646 647 String origline = line; 649 line = origline.toLowerCase(); 650 651 if (line.startsWith("content-type")) { 653 int start = line.indexOf(" "); 654 if (start == -1) { 655 throw new IOException("Content type corrupt: " + origline); 656 } 657 contentType = line.substring(start + 1); 658 } else if (line.length() != 0) { throw new IOException("Malformed line after disposition: " + origline); 660 } 661 662 return contentType; 663 } 664 665 private static long uniqueId = 0; 666 667 private static final synchronized String uniqueId() { 668 uniqueId++; 669 return System.currentTimeMillis() + "." + uniqueId; 670 } 671 672 673 676 class UploadedFile { 677 private String fileName; 678 private String type; 679 private File uploadedFile; 680 681 UploadedFile(String fileName, String type, File f) { 682 this.uploadedFile = f; 683 this.fileName = fileName; 684 this.type = type; 685 } 686 687 690 public String getDir() { 691 if (uploadedFile != null) 692 return uploadedFile.getParentFile().getPath(); 693 else 694 return null; 695 } 696 697 700 public String getFileName() { 701 return fileName; 702 } 703 704 707 public String getContentType() { 708 return type; 709 } 710 711 714 public File getFile() { 715 return uploadedFile; 716 } 717 718 721 public String getId() { 722 if (uploadedFile != null) 723 return uploadedFile.getName(); 724 else 725 return null; 726 } 727 728 729 742 public String toString() { 743 String encoding = getRequest().getCharacterEncoding() != null ? getRequest().getCharacterEncoding() : LocaleCharSet.DEFAULT_ENCODING; 744 745 try { 746 StringBuffer buffer = new StringBuffer (); 747 buffer.append("dir="); 748 buffer.append(URLEncoder.encode(getDir(), encoding)); 749 if (getFileName() != null) { 750 buffer.append("&name="); 751 buffer.append(URLEncoder.encode(getFileName(), encoding)); 752 } 753 if (getContentType() != null) { 754 buffer.append("&type="); 755 buffer.append(URLEncoder.encode(getContentType(), encoding)); 756 } 757 buffer.append("&id="); 758 buffer.append(URLEncoder.encode(getId(), encoding)); 759 760 return buffer.toString(); 761 } catch (UnsupportedEncodingException e) { 762 log.error(getClass().getName(), e); 763 return null; 764 } 765 } 766 } 767 } 768 769 770 771 | Popular Tags |