001package bradleyross.j2ee.servlets; 002import javax.servlet.http.HttpServlet; 003import javax.servlet.http.HttpServletRequest; 004import javax.servlet.http.HttpServletResponse; 005import javax.servlet.ServletConfig; 006import javax.servlet.ServletException; 007import javax.servlet.ServletInputStream; 008import java.io.IOException; 009import java.io.ByteArrayOutputStream; 010import java.io.File; 011import java.io.FileOutputStream; 012import java.io.UnsupportedEncodingException; 013import java.util.Enumeration; 014import java.util.Hashtable; 015import java.util.Vector; 016import java.sql.PreparedStatement; 017import java.sql.SQLException; 018import java.sql.Types; 019import java.text.NumberFormat; 020import bradleyross.library.helpers.StringHelpers; 021import bradleyross.library.database.DatabaseProperties; 022import bradleyross.library.helpers.GenericPrinter; 023// import bradleyross.library.helpers.StringHelpers; 024/** 025 * Start of servlet for testing techniques for uploads. 026 * <ul> 027 * <li><a href="http://www.faqs.org/rfcs/rfc2616.html" target="_blank"> 028 * RFC 2616</a> See section 19.5.1</li> 029 * <li><a href="http://www.faqs.org/rfcs/rfc2388.html" target="_blank"> 030 * RFC 2388 - Returning Values From Forms: multipart/form-data</a></li> 031 * <li><a href="http://www.faqs.org/rfcs/rfc1867.html" target="_blank"> 032 * RFC 1867 - Form-based File Upload in HTML</a></li> 033 * </ul> 034 * <p>The specifications seem to say that the values on the headers may 035 * be tokens or quoted strings.</p> 036 * 037 * <p>It may be necessary to expand this algorithm to handle additional 038 * transfer encoding types. The two mentioned on the web are base64 and 039 * quoted-printable.</p> 040 * <p>MSDN lists the following types: 7bit, 8bit, binary, base64, 041 * quoted-printable, and X-token.</p> 042 * @author Bradley Ross 043 * 044 */ 045public class UploadServlet extends HttpServlet 046{ 047 /** 048 * Methods for formatting integers. 049 * 050 * @author Bradley Ross 051 * 052 */ 053 public class Formatters 054 { 055 /** 056 * Format as a two digit number with leading zeroes. 057 */ 058 protected NumberFormat nf2 = null; 059 /** 060 * Return the two digit formatter. 061 * @return Formatter 062 */ 063 public NumberFormat getNf2() 064 { 065 return nf2; 066 } 067 /** 068 * Format as a four digit number with leading zeroes. 069 */ 070 protected NumberFormat nf4 = null; 071 /** 072 * Return the four digit formatter. 073 * @return Formatter 074 */ 075 public NumberFormat getNf4() 076 { 077 return nf4; 078 } 079 /** 080 * Format as a five digit number with leading zeroes. 081 */ 082 protected NumberFormat nf5 = null; 083 /** 084 * Return the five digit formatter. 085 * @return Formatter 086 */ 087 public NumberFormat getNf5() 088 { 089 return nf5; 090 } 091 /** 092 * Format as a six digit number with leading zeroes. 093 */ 094 protected NumberFormat nf6 = null; 095 /** 096 * Return the six digit formatter. 097 * @return Formatter 098 */ 099 public NumberFormat getNf6() 100 { 101 return nf6; 102 } 103 /** 104 * Format as a eight digit number with leading zeroes. 105 */ 106 protected NumberFormat nf8 = null; 107 /** 108 * Return the eight digit formatter. 109 * @return Formatter 110 */ 111 public NumberFormat getNf8() 112 { 113 return nf8; 114 } 115 public Formatters() 116 { 117 /* 118 * 2 digit formatter 119 */ 120 nf2 = NumberFormat.getIntegerInstance(); 121 nf2.setMinimumIntegerDigits(2); 122 nf2.setMaximumIntegerDigits(2); 123 nf2.setGroupingUsed(false); 124 nf2.setParseIntegerOnly(true); 125 nf2.setMaximumFractionDigits(0); 126 /* 127 * 4 digit formatter 128 */ 129 nf4 = NumberFormat.getIntegerInstance(); 130 nf4.setMinimumIntegerDigits(4); 131 nf4.setMaximumIntegerDigits(4); 132 nf4.setGroupingUsed(false); 133 nf4.setParseIntegerOnly(true); 134 nf4.setMaximumFractionDigits(0); 135 /* 136 * 5 digit formatter 137 */ 138 nf5 = NumberFormat.getIntegerInstance(); 139 nf5.setMinimumIntegerDigits(5); 140 nf5.setMaximumIntegerDigits(5); 141 nf5.setGroupingUsed(false); 142 nf5.setParseIntegerOnly(true); 143 nf5.setMaximumFractionDigits(0); 144 /* 145 * 6 digit formatter 146 */ 147 nf6 = NumberFormat.getIntegerInstance(); 148 nf6.setMinimumIntegerDigits(6); 149 nf6.setMaximumIntegerDigits(6); 150 nf6.setGroupingUsed(false); 151 nf6.setParseIntegerOnly(true); 152 nf6.setMaximumFractionDigits(0); 153 /* 154 * 8 digit formatter 155 */ 156 nf8 = NumberFormat.getIntegerInstance(); 157 nf8.setMinimumIntegerDigits(6); 158 nf8.setMaximumIntegerDigits(6); 159 nf8.setGroupingUsed(false); 160 nf8.setParseIntegerOnly(true); 161 nf8.setMaximumFractionDigits(0); 162 } 163 } 164 /** 165 * Contains information relating to a single HTTP request. 166 * 167 * <p>Since a single object can be used for multiple 168 * simultaneous HTTP requests, information about a 169 * single request can't be placed in the fields relating 170 * to the instance.</p> 171 * <p>By placing the contents of all of the data fields in this object, the 172 * methods can have access to all of the fields without interfering with each other.</p> 173 * <p>Each of the input fields is identified by its name in the HTML tag.</p> 174 * 175 * @author Bradley Ross 176 * 177 */ 178 public class ThisPage extends bradleyross.j2ee.servlets.ThisPage 179 { 180 /** 181 * Contains the various elements from the requesting form. 182 */ 183 protected Hashtable<String, Contents> items = new Hashtable<String, Contents>(); 184 /** 185 * True if final part of request has been 186 * processed. 187 * 188 * @see #getEndOfPacket() 189 * @see #setEndOfPacket(boolean) 190 */ 191 protected boolean endOfPacket = false; 192 /** 193 * Obtain value of ServletInputStream object. 194 * @return ServletInputStream object 195 */ 196 /* public ServletInputStream getInputStream() 197 { 198 return input; 199 } */ 200 201 /** 202 * Sets value of endOfPacket. 203 * 204 * @param value Value for endOfPacket 205 * @see #endOfPacket 206 */ 207 public void setEndOfPacket(boolean value) 208 { 209 endOfPacket = value; 210 } 211 /** 212 * Gets value of endOfPacket. 213 * 214 * @return value of endOfPacket 215 * @see #endOfPacket 216 */ 217 public boolean getEndOfPacket() 218 { 219 return endOfPacket; 220 } 221 /** 222 * Boundary marker for multipart form 223 */ 224 protected String boundary = null; 225 /** 226 * Obtains value of boundary marker 227 * @return Value of boundary marker 228 */ 229 public String getBoundary() 230 { 231 return boundary; 232 } 233 /** 234 * Adds an element to the list associated with the HTTP request. 235 * @param value Element 236 */ 237 public void addElement(Contents value) 238 { 239 items.put(value.getName(), value); 240 } 241 /** 242 * Constructor for object, setting the input and output streams so that the 243 * element can be processed. 244 * @param value1 Request object 245 * @param value2 Response object 246 * @param value3 Servlet configuration object 247 * 248 */ 249 public ThisPage (HttpServletRequest value1, HttpServletResponse value2, ServletConfig value3) 250 throws IOException 251 { 252 super(value1, value2, value3); 253 context = config.getServletContext(); 254 boundary = extractBoundary(getRequest()); 255 input = getRequest().getInputStream(); 256 257 } 258 /** 259 * Return an enumeration containing the names of the elements. 260 * @return Enumeration containing list of names. 261 */ 262 public Enumeration<String> getPartNames() 263 { 264 return items.keys(); 265 } 266 /** 267 * Address to be used for redirecting servlet upon completion. 268 * @see HttpServletResponse#sendRedirect(String) 269 * @see #getRedirectAddress() 270 * @see #setRedirectAddress(String) 271 */ 272 protected String redirectAddress = null; 273 /** 274 * Set the URL for redirecting the servlet upon completion. 275 * @param value URL to be used for redirecting servlet 276 * @see #redirectAddress 277 */ 278 public void setRedirectAddress(String value) 279 { 280 redirectAddress = value; 281 } 282 /** 283 * Get the URL to be used for redirecting the servlet upon completion. 284 * @return URL to be used 285 * @see #redirectAddress 286 */ 287 public String getRedirectAddress() 288 { 289 return redirectAddress; 290 } 291 /** 292 * Retrieve the object associated with an element based on the 293 * element name. 294 * @param key Name of the element in the form 295 * @return Object containing the contents of the element, including 296 * name, MIME type, and contents 297 */ 298 public Contents getElement(String key) 299 { 300 return items.get(key); 301 } 302 /** 303 * Return the contents of one of the elements as 304 * a String object. 305 * 306 * @param key Key value for element 307 * @return String object containing contents 308 */ 309 public String getString(String key) 310 { 311 Contents element = getElement(key); 312 if (element == null) 313 { 314 return null; 315 } 316 byte contents[] = element.getContents(); 317 if (contents == null) 318 { 319 return null; 320 } 321 else if (contents.length == 0) 322 { 323 return null; 324 } 325 String working = new String(contents); 326 return working.trim(); 327 } 328 /** 329 * Return the contents of one of the elements as a byte 330 * array. 331 * 332 * @param key Key value for the element 333 * @return Byte array containing contents of element 334 */ 335 public byte[] getByteArray(String key) 336 { 337 Contents element = getElement(key); 338 if (element == null) 339 { 340 return null; 341 } 342 byte contents[] = element.getContents(); 343 if (contents == null) 344 { 345 return null; 346 } 347 else if (contents.length == 0) 348 { 349 return null; 350 } 351 return contents; 352 } 353 } 354 /** 355 * Contains information on the contents of a single part of a 356 * multipart form. 357 * 358 * <p>See <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2" target="_blank"> 359 * http://www.w3.org/TR/html401/interact/forms.html#h-17.13.42</a> for a discussion 360 * of <i>multipart/form-data</i>.</p> 361 * <p>All lines must end with \r\n. However, binary data may have \r\n in the middle 362 * of the material.</p> 363 * 364 * @author Bradley Ross 365 * 366 */ 367 public class Contents 368 { 369 /** 370 * Name of the part. 371 */ 372 protected String name = null; 373 /** 374 * Filename associated with the part. 375 */ 376 protected String filename = null; 377 /** 378 * MIME code for the data in the part. 379 */ 380 protected String mime = null; 381 /** 382 * Content transfer encoding method for the 383 * element of the transaction. 384 * 385 * <p>According to the documentation, there 386 * are various encoding methods that can be 387 * used such as base64 and quoted-printable. 388 * These have not been seen in the browsers 389 * I have used, but I may have to allow for 390 * these encoding methods.</p> 391 */ 392 protected String transferEncoding = null; 393 /** 394 * The byte array containing the data for this 395 * part. 396 */ 397 protected byte value[] = null; 398 /** 399 * Obtain the name of the part. 400 * @return Name of part 401 */ 402 public String getName() 403 { 404 return name; 405 } 406 /** 407 * Obtain the filename associated with the part. 408 * 409 * <p>If the data was not taken from a file, a null 410 * value is returned.</p> 411 * @return Filename associated with part 412 */ 413 public String getFilename() 414 { 415 return filename; 416 } 417 /** 418 * Obtain mime type for part. 419 * 420 * <p>MIME types would only be associated with data taken from 421 * files.</p> 422 * 423 * @return MIME code 424 */ 425 public String getMime() 426 { 427 return mime; 428 } 429 /** 430 * Obtain the value for content-transfer-encoding. 431 * 432 * @return Type of encoding 433 */ 434 public String getTransferEncoding() 435 { 436 return transferEncoding; 437 } 438 /** 439 * <p>Obtains the byte array that was associated with this 440 * part of the form.</p> 441 * @return byte array 442 */ 443 public byte[] getContents() 444 { 445 return value; 446 } 447 /** 448 * Obtains the size of the byte array that was associated with 449 * this part of the form. 450 * @return Size of byte array 451 */ 452 public int getContentsSize() 453 { 454 if (value == null) 455 { return 0; } 456 return value.length; 457 } 458 /** 459 * Parses header lines in an individual part of a multipart form. 460 * 461 * <p>This algorithm works if the values for the name and filename 462 * are surrounded by double quotes, there are no escaped 463 * characters with the values, and there are no spaces between the 464 * start of the word name or filename and the corresponding closing 465 * quote. It also assumes that the name parameter comes before 466 * the filename parameter. I'm not sure if these assumptions are valid.</p> 467 * @param lineValue Line to be parsed 468 */ 469 public void parseLine(String lineValue) 470 { 471 String line = lineValue; 472 if (line.endsWith("\r\n")) 473 { 474 int length = line.length(); 475 line = line.substring(0, length - 2); 476 } 477 if (line.toLowerCase().startsWith("content-disposition:")) 478 { 479 int start = -1; 480 int end = -1; 481 start = line.toLowerCase().indexOf("name=\""); 482 if (start >= 0) 483 { 484 start = start + 6; 485 end = line.indexOf("\"", start); 486 name = line.substring(start, end); 487 } 488 start = line.toLowerCase().indexOf("filename=\"") ; 489 if (start >= 0) 490 { 491 start = start + 10; 492 end = line.indexOf("\"", start); 493 filename = line.substring(start, end); 494 } 495 } 496 else if (line.toLowerCase().startsWith("content-type:")) 497 { 498 int loc = line.indexOf(":"); 499 mime = line.substring(loc + 1).trim(); 500 } 501 else if (line.toLowerCase().startsWith("content-transfer-encoding:")) 502 { 503 int loc = line.indexOf(":"); 504 transferEncoding = line.substring(loc + 1).trim(); 505 } 506 } 507 /** 508 * Connect the byte array to the object. 509 * @param input Byte array to be attached to object 510 */ 511 public void setContents(byte[] input) 512 { 513 value = input; 514 } 515 public String toString() 516 { 517 StringBuffer working = new StringBuffer(); 518 working.append( "Name: " + name + "; Filename: " + filename + "; MIME: " + mime + 519 "\r\n\r\n"); 520 if (value == null) 521 { 522 return new String(working); 523 } 524 else if (value.length == 0) 525 { 526 return new String(working); 527 } 528 try 529 { 530 String addition = new String(value, "ISO8859_1"); 531 working.append(addition); 532 } 533 catch (UnsupportedEncodingException e) 534 { 535 working.append("Unable to represent byte stream"); 536 } 537 return new String(working); 538 } 539 } 540 /** 541 * Dummy id to satisfy serializable interface 542 */ 543 private static final long serialVersionUID = 1L; 544 /** 545 * Used to control amount of debugging output. 546 */ 547 protected static int debugLevel = 0; 548 /** 549 * ServletConfig object for servlet. 550 */ 551 protected ServletConfig config = null; 552 /** 553 * Called when object for handling HTTP request is created. 554 */ 555 public void init(ServletConfig configIn) throws ServletException 556 { 557 config= configIn; 558 } 559 /** 560 * Process the call to the servlet. 561 * 562 * <p>With regard to reading data from the request, a 563 * {@link javax.servlet.ServletInputStream} object for binary data 564 * can be obtained by {@link HttpServletRequest#getInputStream()} 565 * or a {@link java.io.BufferedReader} can be obtained for character data 566 * by using 567 * {@link HttpServletRequest#getReader}.</p> 568 * <p>With regard to writing data to the response a 569 * {@link javax.servlet.ServletOutputStream} object for binary 570 * data can be obtained using {@link HttpServletResponse#getOutputStream()} 571 * while a {@link java.io.PrintWriter} object can be obtained using 572 * {@link HttpServletResponse#getWriter()}.</p> 573 * <p>The {@link java.io.ByteArrayOutputStream} class can be used as a means of collecting 574 * the bytes contained in the attached file.</p> 575 * <p>It would be desirable to have tests for enctype and method.</p> 576 * 577 * @param req Request object 578 * @param res Response object 579 * @throws IOException 580 */ 581 public void service (HttpServletRequest req, 582 HttpServletResponse res) 583 throws IOException 584 { 585 ThisPage thisPage = new ThisPage(req, res, config); 586 GenericPrinter output = thisPage.getPrinter(); 587 if (!req.getMethod().equalsIgnoreCase("post")) 588 { 589 output.println("<html><head>"); 590 output.println("<title>Must use POST method</title>"); 591 output.println("</head><body>"); 592 output.println("<h1>Must use POST method</h1>"); 593 output.println("<p>Request used " + req.getMethod() + " method</p>"); 594 output.println("<p>Must use POST method</p>"); 595 output.println("</body></html>"); 596 thisPage.sendContents(); 597 return; 598 } 599 else if (req.getHeader("content-type") == null) 600 { 601 output.println("<html><head>"); 602 output.println("<title>Missing content-type header</title>"); 603 output.println("</head><body>"); 604 output.println("<h1>Missing content-type header</h1>"); 605 output.println("<p>Must use content-type header " 606 + "to specify multipart/form-data encoding</p>"); 607 output.println("</body></html>"); 608 thisPage.sendContents(); 609 return; 610 } 611 else if (!req.getHeader("content-type").toLowerCase().startsWith("multipart/form-data")) 612 { 613 output.println("<html><head>"); 614 output.println("<title>Must use multipart/form-data encoding</title>"); 615 output.println("</head><body>"); 616 output.println("<h1>Must use multipart/form-data encoding</h1>"); 617 output.println("<p>content-type is " + req.getHeader("content-type") + " </p>"); 618 output.println("<p>Must use multipart/form-data encoding</p>"); 619 output.println("</body></html>"); 620 thisPage.sendContents(); 621 return; 622 } 623 String boundary = extractBoundary(req); 624 if (boundary == null) 625 { 626 thisPage.addMessage("Unable to extract boundary value"); 627 thisPage.addMessage(req.getHeader("content-type")); 628 thisPage.errorMessage(); 629 return; 630 } 631 int counter = 0; 632 byte buffer[] = new byte[4096]; 633 byte extract[]; 634 int bytesRead = -1; 635 ServletInputStream input = req.getInputStream(); 636 bytesRead = input.readLine(buffer, 0, buffer.length); 637 if (!new String(buffer, 0, bytesRead, "ISO8859_1").startsWith("--" + boundary)) 638 { 639 thisPage.addMessage("Should be separator " + "--" + boundary + " : " 640 +" found " + new String(buffer, 0, bytesRead)); 641 thisPage.errorMessage(); 642 return; 643 } 644 while (true) 645 { 646 counter++; 647 Contents part = new Contents(); 648 thisPage.addMessage("Starting part " + Integer.toString(counter) + " of form"); 649 while (true) 650 { 651 652 bytesRead = input.readLine(buffer, 0, buffer.length); 653 if (bytesRead < 0) 654 { 655 thisPage.addMessage("Unexpected end of packet"); 656 thisPage.errorMessage(); 657 return; 658 } 659 else 660 { 661 String value = new String(buffer, 0, bytesRead, "ISO8859_1"); 662 if (value.endsWith("\r\n")) 663 { 664 value = stripEOL(value); 665 } 666 if (value.length() == 0) 667 { 668 extract = readPart(input, thisPage, part.getTransferEncoding()); 669 if (thisPage.getTerminateRequest()) 670 { 671 return; 672 } 673 part.setContents(extract); 674 thisPage.addElement(part); 675 break; 676 } 677 else 678 { 679 thisPage.addMessage("service method - Parsing line: " + value); 680 part.parseLine(value); 681 } 682 } 683 } 684 if (thisPage.getEndOfPacket()) 685 { 686 break; 687 } 688 } 689 if (thisPage.getTerminateRequest()) 690 { 691 return; 692 } 693 starter(thisPage); 694 if (thisPage.getTerminateRequest()) 695 { 696 return; 697 } 698 processor(thisPage); 699 if (thisPage.getTerminateRequest()) 700 { 701 return; 702 } 703 ender(thisPage); 704 if (thisPage.getTerminateRequest()) 705 { 706 return; 707 } 708 if (thisPage.getRedirectAddress() != null) 709 { 710 thisPage.getResponse().sendRedirect(thisPage.getResponse().encodeRedirectURL(thisPage.getRedirectAddress())); 711 } 712 } 713 /** 714 * Obtain the byte array for this part of the request. 715 * 716 * <p>It may be necessary to modify this code to handle additional encoding 717 * types such as Base64.</p> 718 * 719 * @param input Object from which data is read 720 * @param thisPage Object containing information for this request 721 * @return Byte array for this part of request 722 * @throws IOException 723 */ 724 protected byte[] readPart(ServletInputStream input, ThisPage thisPage, String encoding) 725 throws IOException 726 { 727 HttpServletRequest req = thisPage.getRequest(); 728 String boundary = extractBoundary(req); 729 byte buffer[] = new byte[4096]; 730 int bytesRead = -1; 731 ByteArrayOutputStream working = null; 732 boolean pendingRN = false; 733 working = new ByteArrayOutputStream(); 734 /* 735 * Read body of part 736 */ 737 while (true) 738 { 739 bytesRead = input.readLine(buffer, 0, buffer.length); 740 if (bytesRead < 0) 741 { 742 thisPage.addMessage("readPart method - Section of form not properly ended"); 743 thisPage.errorMessage(); 744 return null; 745 } 746 else if (bytesRead == 0) 747 { 748 thisPage.addMessage("readPart method - Read yielded 0 characters"); 749 thisPage.errorMessage(); 750 return null; 751 } 752 else if (bytesRead == 1 && buffer[0] == '\n') 753 { 754 if (pendingRN) 755 { 756 working.write('\r'); 757 working.write('\n'); 758 pendingRN = false; 759 } 760 working.write(buffer[0]); 761 } 762 else if (new String(buffer, 0, bytesRead).equals("--" + boundary + "\r\n")) 763 { 764 if (!pendingRN) 765 { 766 thisPage.addMessage("readPart method - Boundary reached without preceding end of line"); 767 thisPage.errorMessage(); 768 return null; 769 } 770 if (working.size() == 0) 771 { 772 return null; 773 } 774 return working.toByteArray(); 775 } 776 else if (new String(buffer, 0, bytesRead).equals("--" + boundary + "--\r\n")) 777 { 778 thisPage.setEndOfPacket(true); 779 if (!pendingRN) 780 { 781 thisPage.addMessage("readPart method - Final boundary reached with preceding end of line"); 782 thisPage.errorMessage(); 783 return null; 784 } 785 if (working.size() == 0) 786 { 787 return null; 788 } 789 return working.toByteArray(); 790 } 791 else if (buffer[bytesRead - 2] == '\r' && buffer[bytesRead - 1] == '\n') 792 { 793 if (pendingRN) 794 { 795 working.write('\r'); 796 working.write('\n'); 797 } 798 if (bytesRead > 2) 799 { 800 working.write(buffer, 0, bytesRead - 2); 801 } 802 pendingRN = true; 803 } 804 else if (buffer[bytesRead - 1] == '\r') 805 { 806 if (pendingRN) 807 { 808 working.write('\r'); 809 working.write('\n'); 810 pendingRN = false; 811 } 812 if (bytesRead > 1) 813 { 814 working.write(buffer, 0, bytesRead - 1); 815 } 816 int nextChar = input.read(); 817 if (nextChar == '\n') 818 { 819 pendingRN = true; 820 } 821 else 822 { 823 working.write('\r'); 824 working.write(nextChar); 825 } 826 } 827 else 828 { 829 if (pendingRN) 830 { 831 working.write('\r'); 832 working.write('\n'); 833 pendingRN=false; 834 } 835 working.write(buffer, 0, bytesRead); 836 } 837 } 838 } 839 /** 840 * Strips end of line characters from a character string. 841 * 842 * @param input String to be processed 843 * @return String with EOL characters stripped off 844 */ 845 protected String stripEOL(String input) 846 { 847 if (input == null) 848 { 849 return (String) null; 850 } 851 String working = input; 852 if (working.endsWith("\r\n") || working.endsWith("\n\r")) 853 { 854 working = working.substring(0, working.length() - 2); 855 } 856 else if (working.endsWith("\r") || working.endsWith("\n")) 857 { 858 working = working.substring(0, working.length() - 1); 859 } 860 return working; 861 } 862 /** 863 * Extracts the value of the boundary string from the header. 864 * @param req Request object 865 * @return Boundary string 866 */ 867 public String extractBoundary(HttpServletRequest req) 868 { 869 String working = null; 870 working = req.getHeader("content-type"); 871 if (working == null) 872 { 873 return null; 874 } 875 int loc = working.indexOf("boundary="); 876 if ( loc < 0) 877 { 878 return null; 879 } 880 else 881 { 882 working = working.substring(loc + 9); 883 } 884 loc = working.indexOf(";"); 885 if (loc == 0) 886 { 887 return null; 888 } 889 else if (loc > 0) 890 { 891 working = working.substring(0, loc); 892 } 893 return working; 894 } 895 /** 896 * Utility method for moving the contents of one of the parts into 897 * a prepared statement as a string object. 898 * @param data Data connection object. 899 * @param stmt Prepared statement 900 * @param thisPage Object representing this request 901 * @param position Position of the value in the prepared statement's argument list 902 * @param element Name of the part in the multi-part form 903 * @param convertFlag True means that value should be converted to upper case 904 * @param SQLType Type of SQL column (Types.CHAR or Types.VARCHAR) 905 * @throws SQLException 906 */ 907 public void loadByteArray(DatabaseProperties data, PreparedStatement stmt, ThisPage thisPage, 908 int position, String element, boolean convertFlag, int SQLType) throws SQLException 909 { 910 byte contents[] = null; 911 Contents portion = thisPage.getElement(element); 912 if (portion == null) 913 { 914 stmt.setNull(position, SQLType); 915 return; 916 } 917 contents = thisPage.getElement(element).getContents(); 918 if (contents == null) 919 { 920 stmt.setNull(position, SQLType); 921 } 922 else if (contents.length == 0) 923 { 924 stmt.setNull(position, SQLType); 925 } 926 else 927 { 928 String working = new String(contents).trim(); 929 if (convertFlag) 930 { 931 working = working.toUpperCase(); 932 } 933 if (working.length() == 0) 934 { 935 stmt.setNull(position, SQLType); 936 } 937 else 938 { 939 stmt.setString(position, working); 940 } 941 } 942 } 943 /** 944 * Utility method for moving the contents of one of the parts into 945 * a prepared statement as a string object. 946 * @param data Data connection object. 947 * @param stmt Prepared statement 948 * @param thisPage Object representing this request 949 * @param position Position of the value in the prepared statement's argument list 950 * @param element Name of the part in the multi-part form 951 * @param convertFlag True means that value should be converted to upper case 952 * @throws SQLException 953 */ 954 public void loadByteArray(DatabaseProperties data, PreparedStatement stmt, ThisPage thisPage, 955 int position, String element, boolean convertFlag) throws SQLException 956 { 957 loadByteArray(data, stmt, thisPage, position, element, convertFlag, Types.VARCHAR); 958 } 959 /** 960 * Utility method for moving the contents of one of the parts into 961 * a prepared statement as a string object. 962 * @param data Data connection object. 963 * @param stmt Prepared statement 964 * @param thisPage Object representing this request 965 * @param position Position of the value in the prepared statement's argument list 966 * @param element Name of the part in the multi-part form 967 * @throws SQLException 968 */ 969 public void loadByteArray(DatabaseProperties data, PreparedStatement stmt, ThisPage thisPage, 970 int position, String element) throws SQLException 971 { 972 loadByteArray(data, stmt, thisPage, position, element, false, Types.VARCHAR); 973 } 974 /** 975 * Perform common operations on the contents of the 976 * request packet before running the main program. 977 * 978 * <p>The intent is to override this method in a subclass 979 * that will then be subclassed to handle the servlets 980 * for the various web pages.</p> 981 * 982 * @param thisPage Information on this HTTP request 983 */ 984 protected void starter(ThisPage thisPage) throws IOException 985 { 986 ; 987 } 988 /** 989 * Perform common operations on the contents of the request packet 990 * to be carried out after running the main program. 991 * 992 * @param thisPage Information on this HTTP transaction 993 */ 994 protected void ender(ThisPage thisPage) throws IOException 995 { 996 ; 997 } 998 /** 999 * This method generates the web page based on the contents 1000 * of the request packet. 1001 * 1002 * <p>This method will be overwritten to enable various actions 1003 * to take place as the information is uploaded.</p> 1004 * <p>For the result of File requests, write the contents to a file.</p> 1005 * <p>I am having a problem with the loading of binary files.</p> 1006 * @param thisPage Information on this HTTP request 1007 */ 1008 protected void processor(ThisPage thisPage) throws IOException 1009 { 1010 Enumeration<String> keys = thisPage.getPartNames(); 1011 HttpServletRequest req = thisPage.getRequest(); 1012 ServletConfig config = thisPage.getConfig(); 1013 String targetDirectory = config.getInitParameter("directory"); 1014 GenericPrinter output = thisPage.getPrinter(); 1015 output.println("<html><head>"); 1016 output.println("<title>Dummy Upload Program</title>"); 1017 output.println("</head><body>"); 1018 output.println("<p>It is assumed that the processor method of the "); 1019 output.println("bradleyross.library.servlets.UploadServlet class will "); 1020 output.println("be overridden to provide the desired function. This "); 1021 output.println("sample version is designed for testing the applications "); 1022 output.println("and to allow demonstration of the capabilities.</p>"); 1023 output.println("<p>Target directory for upload tests is " + targetDirectory + "</p>"); 1024 output.println("<h2>Headers</h2>"); 1025 output.println("<table border=\"1\">"); 1026 Enumeration<?> list1 = req.getHeaderNames(); 1027 while (list1.hasMoreElements()) 1028 { 1029 String name = (String) list1.nextElement(); 1030 Enumeration<?> list2 = req.getHeaders(name); 1031 while (list2.hasMoreElements()) 1032 { 1033 String value = (String) list2.nextElement(); 1034 output.println("<tr><td>" + name + "</td><td>" + value + "</td></tr>"); 1035 } 1036 } 1037 output.println("</table>"); 1038 output.println("<h2>Parts of form</h2>"); 1039 output.println("<p>The following are the parts of the multipart form</p>"); 1040 output.println("<ul>"); 1041 while (keys.hasMoreElements()) 1042 { 1043 String name = keys.nextElement(); 1044 String mime = thisPage.getElement(name).getMime(); 1045 String fileName = thisPage.getElement(name).getFilename(); 1046 String encoding = thisPage.getElement(name).getTransferEncoding(); 1047 output.println("<li><p>" + name + "</p>"); 1048 if (fileName == null) 1049 { 1050 output.println("<p>Filename not specified</p>"); 1051 } 1052 else 1053 { 1054 output.println("<p>Filename is " + fileName + "</p>"); 1055 } 1056 if (mime == null) 1057 { 1058 output.println("<p>Content type not specified</p>"); 1059 mime = new String(); 1060 } 1061 else 1062 { 1063 output.println("<p>Content-type: " + mime + "</p>"); 1064 } 1065 if (encoding == null) 1066 { 1067 output.println("<p>Transfer encoding not specified</p>"); 1068 } 1069 else 1070 { 1071 output.println("<p>Content-transfer-encoding: " + encoding + "</p>"); 1072 } 1073 if (mime.toUpperCase().startsWith("TEXT")) 1074 { 1075 output.println("<p>" + StringHelpers.escapeHTML(thisPage.getElement(name).toString()) 1076 + "</p></li>"); 1077 } 1078 output.println("<p>Size of contents: " + Integer.toString(thisPage.getElement(name).getContentsSize())); 1079 if (fileName != null && targetDirectory != null) 1080 { 1081 try 1082 { 1083 boolean validEntry = true; 1084 File outputFile = null; 1085 if (targetDirectory.length() == 0 || fileName.length() == 0) 1086 { 1087 validEntry = false; 1088 } 1089 if (validEntry) 1090 { 1091 outputFile = new File (targetDirectory, fileName); 1092 output.println("<p>Name of file is " + StringHelpers.escapeHTML(outputFile.getCanonicalPath()) + "</p>"); 1093 } 1094 if (!validEntry) 1095 { ; } 1096 else if (outputFile == null) 1097 { 1098 output.println("<p>Unable to open output file</p>"); 1099 } 1100 else 1101 { 1102 FileOutputStream outputStream = new FileOutputStream(outputFile); 1103 byte transfer[] = thisPage.getElement(name).getContents(); 1104 if (transfer == null) 1105 { 1106 output.println("<p>Unable to get file contents</p>"); 1107 } 1108 else 1109 { 1110 outputStream.write(thisPage.getElement(name).getContents()); 1111 } 1112 outputStream.close(); 1113 } 1114 } 1115 catch (IOException e) 1116 { 1117 output.println("<p>Error while writing file</p>"); 1118 output.println(StringHelpers.escapeHTML("<p>" + e.getClass().getName() + " " + 1119 e.getMessage() + "</p>")); 1120 } 1121 } 1122 else 1123 { ; } 1124 } 1125 output.println("</ul>"); 1126 output.println("<h2>Messages</h2>"); 1127 output.println("<p>These messages are normally printed only if an error occurs during the processing "); 1128 output.println("of the HTTP transaction. They are included here to test the behavior of the servlet.</p><ol>"); 1129 Vector<String> messages = thisPage.getMessageList(); 1130 messages.trimToSize(); 1131 for (int i = 0; i < messages.size(); i++) 1132 { 1133 output.println("<li><p>" + StringHelpers.escapeHTML(messages.elementAt(i)) + "</p></li>"); 1134 } 1135 output.println("</ol>"); 1136 output.println("</body></html>"); 1137 thisPage.sendContents(); 1138 } 1139}