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 * @throws IOException if io errors 248 * 249 */ 250 public ThisPage (HttpServletRequest value1, HttpServletResponse value2, ServletConfig value3) 251 throws IOException 252 { 253 super(value1, value2, value3); 254 context = config.getServletContext(); 255 boundary = extractBoundary(getRequest()); 256 input = getRequest().getInputStream(); 257 258 } 259 /** 260 * Return an enumeration containing the names of the elements. 261 * @return Enumeration containing list of names. 262 */ 263 public Enumeration<String> getPartNames() 264 { 265 return items.keys(); 266 } 267 /** 268 * Address to be used for redirecting servlet upon completion. 269 * @see HttpServletResponse#sendRedirect(String) 270 * @see #getRedirectAddress() 271 * @see #setRedirectAddress(String) 272 */ 273 protected String redirectAddress = null; 274 /** 275 * Set the URL for redirecting the servlet upon completion. 276 * @param value URL to be used for redirecting servlet 277 * @see #redirectAddress 278 */ 279 public void setRedirectAddress(String value) 280 { 281 redirectAddress = value; 282 } 283 /** 284 * Get the URL to be used for redirecting the servlet upon completion. 285 * @return URL to be used 286 * @see #redirectAddress 287 */ 288 public String getRedirectAddress() 289 { 290 return redirectAddress; 291 } 292 /** 293 * Retrieve the object associated with an element based on the 294 * element name. 295 * @param key Name of the element in the form 296 * @return Object containing the contents of the element, including 297 * name, MIME type, and contents 298 */ 299 public Contents getElement(String key) 300 { 301 return items.get(key); 302 } 303 /** 304 * Return the contents of one of the elements as 305 * a String object. 306 * 307 * @param key Key value for element 308 * @return String object containing contents 309 */ 310 public String getString(String key) 311 { 312 Contents element = getElement(key); 313 if (element == null) 314 { 315 return null; 316 } 317 byte contents[] = element.getContents(); 318 if (contents == null) 319 { 320 return null; 321 } 322 else if (contents.length == 0) 323 { 324 return null; 325 } 326 String working = new String(contents); 327 return working.trim(); 328 } 329 /** 330 * Return the contents of one of the elements as a byte 331 * array. 332 * 333 * @param key Key value for the element 334 * @return Byte array containing contents of element 335 */ 336 public byte[] getByteArray(String key) 337 { 338 Contents element = getElement(key); 339 if (element == null) 340 { 341 return null; 342 } 343 byte contents[] = element.getContents(); 344 if (contents == null) 345 { 346 return null; 347 } 348 else if (contents.length == 0) 349 { 350 return null; 351 } 352 return contents; 353 } 354 } 355 /** 356 * Contains information on the contents of a single part of a 357 * multipart form. 358 * 359 * <p>See <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2" target="_blank"> 360 * http://www.w3.org/TR/html401/interact/forms.html#h-17.13.42</a> for a discussion 361 * of <i>multipart/form-data</i>.</p> 362 * <p>All lines must end with \r\n. However, binary data may have \r\n in the middle 363 * of the material.</p> 364 * 365 * @author Bradley Ross 366 * 367 */ 368 public class Contents 369 { 370 /** 371 * Name of the part. 372 */ 373 protected String name = null; 374 /** 375 * Filename associated with the part. 376 */ 377 protected String filename = null; 378 /** 379 * MIME code for the data in the part. 380 */ 381 protected String mime = null; 382 /** 383 * Content transfer encoding method for the 384 * element of the transaction. 385 * 386 * <p>According to the documentation, there 387 * are various encoding methods that can be 388 * used such as base64 and quoted-printable. 389 * These have not been seen in the browsers 390 * I have used, but I may have to allow for 391 * these encoding methods.</p> 392 */ 393 protected String transferEncoding = null; 394 /** 395 * The byte array containing the data for this 396 * part. 397 */ 398 protected byte value[] = null; 399 /** 400 * Obtain the name of the part. 401 * @return Name of part 402 */ 403 public String getName() 404 { 405 return name; 406 } 407 /** 408 * Obtain the filename associated with the part. 409 * 410 * <p>If the data was not taken from a file, a null 411 * value is returned.</p> 412 * @return Filename associated with part 413 */ 414 public String getFilename() 415 { 416 return filename; 417 } 418 /** 419 * Obtain mime type for part. 420 * 421 * <p>MIME types would only be associated with data taken from 422 * files.</p> 423 * 424 * @return MIME code 425 */ 426 public String getMime() 427 { 428 return mime; 429 } 430 /** 431 * Obtain the value for content-transfer-encoding. 432 * 433 * @return Type of encoding 434 */ 435 public String getTransferEncoding() 436 { 437 return transferEncoding; 438 } 439 /** 440 * <p>Obtains the byte array that was associated with this 441 * part of the form.</p> 442 * @return byte array 443 */ 444 public byte[] getContents() 445 { 446 return value; 447 } 448 /** 449 * Obtains the size of the byte array that was associated with 450 * this part of the form. 451 * @return Size of byte array 452 */ 453 public int getContentsSize() 454 { 455 if (value == null) 456 { return 0; } 457 return value.length; 458 } 459 /** 460 * Parses header lines in an individual part of a multipart form. 461 * 462 * <p>This algorithm works if the values for the name and filename 463 * are surrounded by double quotes, there are no escaped 464 * characters with the values, and there are no spaces between the 465 * start of the word name or filename and the corresponding closing 466 * quote. It also assumes that the name parameter comes before 467 * the filename parameter. I'm not sure if these assumptions are valid.</p> 468 * @param lineValue Line to be parsed 469 */ 470 public void parseLine(String lineValue) 471 { 472 String line = lineValue; 473 if (line.endsWith("\r\n")) 474 { 475 int length = line.length(); 476 line = line.substring(0, length - 2); 477 } 478 if (line.toLowerCase().startsWith("content-disposition:")) 479 { 480 int start = -1; 481 int end = -1; 482 start = line.toLowerCase().indexOf("name=\""); 483 if (start >= 0) 484 { 485 start = start + 6; 486 end = line.indexOf("\"", start); 487 name = line.substring(start, end); 488 } 489 start = line.toLowerCase().indexOf("filename=\"") ; 490 if (start >= 0) 491 { 492 start = start + 10; 493 end = line.indexOf("\"", start); 494 filename = line.substring(start, end); 495 } 496 } 497 else if (line.toLowerCase().startsWith("content-type:")) 498 { 499 int loc = line.indexOf(":"); 500 mime = line.substring(loc + 1).trim(); 501 } 502 else if (line.toLowerCase().startsWith("content-transfer-encoding:")) 503 { 504 int loc = line.indexOf(":"); 505 transferEncoding = line.substring(loc + 1).trim(); 506 } 507 } 508 /** 509 * Connect the byte array to the object. 510 * @param input Byte array to be attached to object 511 */ 512 public void setContents(byte[] input) 513 { 514 value = input; 515 } 516 public String toString() 517 { 518 StringBuffer working = new StringBuffer(); 519 working.append( "Name: " + name + "; Filename: " + filename + "; MIME: " + mime + 520 "\r\n\r\n"); 521 if (value == null) 522 { 523 return new String(working); 524 } 525 else if (value.length == 0) 526 { 527 return new String(working); 528 } 529 try 530 { 531 String addition = new String(value, "ISO8859_1"); 532 working.append(addition); 533 } 534 catch (UnsupportedEncodingException e) 535 { 536 working.append("Unable to represent byte stream"); 537 } 538 return new String(working); 539 } 540 } 541 /** 542 * Dummy id to satisfy serializable interface 543 */ 544 private static final long serialVersionUID = 1L; 545 /** 546 * Used to control amount of debugging output. 547 */ 548 protected static int debugLevel = 0; 549 /** 550 * ServletConfig object for servlet. 551 */ 552 protected ServletConfig config = null; 553 /** 554 * Called when object for handling HTTP request is created. 555 */ 556 public void init(ServletConfig configIn) throws ServletException 557 { 558 config= configIn; 559 } 560 /** 561 * Process the call to the servlet. 562 * 563 * <p>With regard to reading data from the request, a 564 * {@link javax.servlet.ServletInputStream} object for binary data 565 * can be obtained by {@link HttpServletRequest#getInputStream()} 566 * or a {@link java.io.BufferedReader} can be obtained for character data 567 * by using 568 * {@link HttpServletRequest#getReader}.</p> 569 * <p>With regard to writing data to the response a 570 * {@link javax.servlet.ServletOutputStream} object for binary 571 * data can be obtained using {@link HttpServletResponse#getOutputStream()} 572 * while a {@link java.io.PrintWriter} object can be obtained using 573 * {@link HttpServletResponse#getWriter()}.</p> 574 * <p>The {@link java.io.ByteArrayOutputStream} class can be used as a means of collecting 575 * the bytes contained in the attached file.</p> 576 * <p>It would be desirable to have tests for enctype and method.</p> 577 * 578 * @param req Request object 579 * @param res Response object 580 * @throws IOException if io problems 581 */ 582 public void service (HttpServletRequest req, 583 HttpServletResponse res) 584 throws IOException 585 { 586 ThisPage thisPage = new ThisPage(req, res, config); 587 GenericPrinter output = thisPage.getPrinter(); 588 if (!req.getMethod().equalsIgnoreCase("post")) 589 { 590 output.println("<html><head>"); 591 output.println("<title>Must use POST method</title>"); 592 output.println("</head><body>"); 593 output.println("<h1>Must use POST method</h1>"); 594 output.println("<p>Request used " + req.getMethod() + " method</p>"); 595 output.println("<p>Must use POST method</p>"); 596 output.println("</body></html>"); 597 thisPage.sendContents(); 598 return; 599 } 600 else if (req.getHeader("content-type") == null) 601 { 602 output.println("<html><head>"); 603 output.println("<title>Missing content-type header</title>"); 604 output.println("</head><body>"); 605 output.println("<h1>Missing content-type header</h1>"); 606 output.println("<p>Must use content-type header " 607 + "to specify multipart/form-data encoding</p>"); 608 output.println("</body></html>"); 609 thisPage.sendContents(); 610 return; 611 } 612 else if (!req.getHeader("content-type").toLowerCase().startsWith("multipart/form-data")) 613 { 614 output.println("<html><head>"); 615 output.println("<title>Must use multipart/form-data encoding</title>"); 616 output.println("</head><body>"); 617 output.println("<h1>Must use multipart/form-data encoding</h1>"); 618 output.println("<p>content-type is " + req.getHeader("content-type") + " </p>"); 619 output.println("<p>Must use multipart/form-data encoding</p>"); 620 output.println("</body></html>"); 621 thisPage.sendContents(); 622 return; 623 } 624 String boundary = extractBoundary(req); 625 if (boundary == null) 626 { 627 thisPage.addMessage("Unable to extract boundary value"); 628 thisPage.addMessage(req.getHeader("content-type")); 629 thisPage.errorMessage(); 630 return; 631 } 632 int counter = 0; 633 byte buffer[] = new byte[4096]; 634 byte extract[]; 635 int bytesRead = -1; 636 ServletInputStream input = req.getInputStream(); 637 bytesRead = input.readLine(buffer, 0, buffer.length); 638 if (!new String(buffer, 0, bytesRead, "ISO8859_1").startsWith("--" + boundary)) 639 { 640 thisPage.addMessage("Should be separator " + "--" + boundary + " : " 641 +" found " + new String(buffer, 0, bytesRead)); 642 thisPage.errorMessage(); 643 return; 644 } 645 while (true) 646 { 647 counter++; 648 Contents part = new Contents(); 649 thisPage.addMessage("Starting part " + Integer.toString(counter) + " of form"); 650 while (true) 651 { 652 653 bytesRead = input.readLine(buffer, 0, buffer.length); 654 if (bytesRead < 0) 655 { 656 thisPage.addMessage("Unexpected end of packet"); 657 thisPage.errorMessage(); 658 return; 659 } 660 else 661 { 662 String value = new String(buffer, 0, bytesRead, "ISO8859_1"); 663 if (value.endsWith("\r\n")) 664 { 665 value = stripEOL(value); 666 } 667 if (value.length() == 0) 668 { 669 extract = readPart(input, thisPage, part.getTransferEncoding()); 670 if (thisPage.getTerminateRequest()) 671 { 672 return; 673 } 674 part.setContents(extract); 675 thisPage.addElement(part); 676 break; 677 } 678 else 679 { 680 thisPage.addMessage("service method - Parsing line: " + value); 681 part.parseLine(value); 682 } 683 } 684 } 685 if (thisPage.getEndOfPacket()) 686 { 687 break; 688 } 689 } 690 if (thisPage.getTerminateRequest()) 691 { 692 return; 693 } 694 starter(thisPage); 695 if (thisPage.getTerminateRequest()) 696 { 697 return; 698 } 699 processor(thisPage); 700 if (thisPage.getTerminateRequest()) 701 { 702 return; 703 } 704 ender(thisPage); 705 if (thisPage.getTerminateRequest()) 706 { 707 return; 708 } 709 if (thisPage.getRedirectAddress() != null) 710 { 711 thisPage.getResponse().sendRedirect(thisPage.getResponse().encodeRedirectURL(thisPage.getRedirectAddress())); 712 } 713 } 714 /** 715 * Obtain the byte array for this part of the request. 716 * 717 * <p>It may be necessary to modify this code to handle additional encoding 718 * types such as Base64.</p> 719 * 720 * @param input Object from which data is read 721 * @param thisPage Object containing information for this request 722 * @param encoding type of character encoding to be used 723 * @return Byte array for this part of request 724 * @throws IOException if io errors 725 */ 726 protected byte[] readPart(ServletInputStream input, ThisPage thisPage, String encoding) 727 throws IOException 728 { 729 HttpServletRequest req = thisPage.getRequest(); 730 String boundary = extractBoundary(req); 731 byte buffer[] = new byte[4096]; 732 int bytesRead = -1; 733 ByteArrayOutputStream working = null; 734 boolean pendingRN = false; 735 working = new ByteArrayOutputStream(); 736 /* 737 * Read body of part 738 */ 739 while (true) 740 { 741 bytesRead = input.readLine(buffer, 0, buffer.length); 742 if (bytesRead < 0) 743 { 744 thisPage.addMessage("readPart method - Section of form not properly ended"); 745 thisPage.errorMessage(); 746 return null; 747 } 748 else if (bytesRead == 0) 749 { 750 thisPage.addMessage("readPart method - Read yielded 0 characters"); 751 thisPage.errorMessage(); 752 return null; 753 } 754 else if (bytesRead == 1 && buffer[0] == '\n') 755 { 756 if (pendingRN) 757 { 758 working.write('\r'); 759 working.write('\n'); 760 pendingRN = false; 761 } 762 working.write(buffer[0]); 763 } 764 else if (new String(buffer, 0, bytesRead).equals("--" + boundary + "\r\n")) 765 { 766 if (!pendingRN) 767 { 768 thisPage.addMessage("readPart method - Boundary reached without preceding end of line"); 769 thisPage.errorMessage(); 770 return null; 771 } 772 if (working.size() == 0) 773 { 774 return null; 775 } 776 return working.toByteArray(); 777 } 778 else if (new String(buffer, 0, bytesRead).equals("--" + boundary + "--\r\n")) 779 { 780 thisPage.setEndOfPacket(true); 781 if (!pendingRN) 782 { 783 thisPage.addMessage("readPart method - Final boundary reached with preceding end of line"); 784 thisPage.errorMessage(); 785 return null; 786 } 787 if (working.size() == 0) 788 { 789 return null; 790 } 791 return working.toByteArray(); 792 } 793 else if (buffer[bytesRead - 2] == '\r' && buffer[bytesRead - 1] == '\n') 794 { 795 if (pendingRN) 796 { 797 working.write('\r'); 798 working.write('\n'); 799 } 800 if (bytesRead > 2) 801 { 802 working.write(buffer, 0, bytesRead - 2); 803 } 804 pendingRN = true; 805 } 806 else if (buffer[bytesRead - 1] == '\r') 807 { 808 if (pendingRN) 809 { 810 working.write('\r'); 811 working.write('\n'); 812 pendingRN = false; 813 } 814 if (bytesRead > 1) 815 { 816 working.write(buffer, 0, bytesRead - 1); 817 } 818 int nextChar = input.read(); 819 if (nextChar == '\n') 820 { 821 pendingRN = true; 822 } 823 else 824 { 825 working.write('\r'); 826 working.write(nextChar); 827 } 828 } 829 else 830 { 831 if (pendingRN) 832 { 833 working.write('\r'); 834 working.write('\n'); 835 pendingRN=false; 836 } 837 working.write(buffer, 0, bytesRead); 838 } 839 } 840 } 841 /** 842 * Strips end of line characters from a character string. 843 * 844 * @param input String to be processed 845 * @return String with EOL characters stripped off 846 */ 847 protected String stripEOL(String input) 848 { 849 if (input == null) 850 { 851 return (String) null; 852 } 853 String working = input; 854 if (working.endsWith("\r\n") || working.endsWith("\n\r")) 855 { 856 working = working.substring(0, working.length() - 2); 857 } 858 else if (working.endsWith("\r") || working.endsWith("\n")) 859 { 860 working = working.substring(0, working.length() - 1); 861 } 862 return working; 863 } 864 /** 865 * Extracts the value of the boundary string from the header. 866 * @param req Request object 867 * @return Boundary string 868 */ 869 public String extractBoundary(HttpServletRequest req) 870 { 871 String working = null; 872 working = req.getHeader("content-type"); 873 if (working == null) 874 { 875 return null; 876 } 877 int loc = working.indexOf("boundary="); 878 if ( loc < 0) 879 { 880 return null; 881 } 882 else 883 { 884 working = working.substring(loc + 9); 885 } 886 loc = working.indexOf(";"); 887 if (loc == 0) 888 { 889 return null; 890 } 891 else if (loc > 0) 892 { 893 working = working.substring(0, loc); 894 } 895 return working; 896 } 897 /** 898 * Utility method for moving the contents of one of the parts into 899 * a prepared statement as a string object. 900 * @param data Data connection object. 901 * @param stmt Prepared statement 902 * @param thisPage Object representing this request 903 * @param position Position of the value in the prepared statement's argument list 904 * @param element Name of the part in the multi-part form 905 * @param convertFlag True means that value should be converted to upper case 906 * @param SQLType Type of SQL column (Types.CHAR or Types.VARCHAR) 907 * @throws SQLException if database errors 908 */ 909 public void loadByteArray(DatabaseProperties data, PreparedStatement stmt, ThisPage thisPage, 910 int position, String element, boolean convertFlag, int SQLType) throws SQLException 911 { 912 byte contents[] = null; 913 Contents portion = thisPage.getElement(element); 914 if (portion == null) 915 { 916 stmt.setNull(position, SQLType); 917 return; 918 } 919 contents = thisPage.getElement(element).getContents(); 920 if (contents == null) 921 { 922 stmt.setNull(position, SQLType); 923 } 924 else if (contents.length == 0) 925 { 926 stmt.setNull(position, SQLType); 927 } 928 else 929 { 930 String working = new String(contents).trim(); 931 if (convertFlag) 932 { 933 working = working.toUpperCase(); 934 } 935 if (working.length() == 0) 936 { 937 stmt.setNull(position, SQLType); 938 } 939 else 940 { 941 stmt.setString(position, working); 942 } 943 } 944 } 945 /** 946 * Utility method for moving the contents of one of the parts into 947 * a prepared statement as a string object. 948 * @param data Data connection object. 949 * @param stmt Prepared statement 950 * @param thisPage Object representing this request 951 * @param position Position of the value in the prepared statement's argument list 952 * @param element Name of the part in the multi-part form 953 * @param convertFlag True means that value should be converted to upper case 954 * @throws SQLException if database errors 955 */ 956 public void loadByteArray(DatabaseProperties data, PreparedStatement stmt, ThisPage thisPage, 957 int position, String element, boolean convertFlag) throws SQLException 958 { 959 loadByteArray(data, stmt, thisPage, position, element, convertFlag, Types.VARCHAR); 960 } 961 /** 962 * Utility method for moving the contents of one of the parts into 963 * a prepared statement as a string object. 964 * @param data Data connection object. 965 * @param stmt Prepared statement 966 * @param thisPage Object representing this request 967 * @param position Position of the value in the prepared statement's argument list 968 * @param element Name of the part in the multi-part form 969 * @throws SQLException if database errors 970 */ 971 public void loadByteArray(DatabaseProperties data, PreparedStatement stmt, ThisPage thisPage, 972 int position, String element) throws SQLException 973 { 974 loadByteArray(data, stmt, thisPage, position, element, false, Types.VARCHAR); 975 } 976 /** 977 * Perform common operations on the contents of the 978 * request packet before running the main program. 979 * 980 * <p>The intent is to override this method in a subclass 981 * that will then be subclassed to handle the servlets 982 * for the various web pages.</p> 983 * 984 * @param thisPage Information on this HTTP request 985 * @throws IOException if io errors 986 */ 987 protected void starter(ThisPage thisPage) throws IOException 988 { 989 ; 990 } 991 /** 992 * Perform common operations on the contents of the request packet 993 * to be carried out after running the main program. 994 * 995 * @param thisPage Information on this HTTP transaction 996 * 997 */ 998 protected void ender(ThisPage thisPage) 999 { 1000 ; 1001 } 1002 /** 1003 * This method generates the web page based on the contents 1004 * of the request packet. 1005 * 1006 * <p>This method will be overwritten to enable various actions 1007 * to take place as the information is uploaded.</p> 1008 * <p>For the result of File requests, write the contents to a file.</p> 1009 * <p>I am having a problem with the loading of binary files.</p> 1010 * @param thisPage Information on this HTTP request 1011 * @throws IOException if io errors 1012 */ 1013 protected void processor(ThisPage thisPage) throws IOException 1014 { 1015 Enumeration<String> keys = thisPage.getPartNames(); 1016 HttpServletRequest req = thisPage.getRequest(); 1017 ServletConfig config = thisPage.getConfig(); 1018 String targetDirectory = config.getInitParameter("directory"); 1019 GenericPrinter output = thisPage.getPrinter(); 1020 output.println("<html><head>"); 1021 output.println("<title>Dummy Upload Program</title>"); 1022 output.println("</head><body>"); 1023 output.println("<p>It is assumed that the processor method of the "); 1024 output.println("bradleyross.library.servlets.UploadServlet class will "); 1025 output.println("be overridden to provide the desired function. This "); 1026 output.println("sample version is designed for testing the applications "); 1027 output.println("and to allow demonstration of the capabilities.</p>"); 1028 output.println("<p>Target directory for upload tests is " + targetDirectory + "</p>"); 1029 output.println("<h2>Headers</h2>"); 1030 output.println("<table border=\"1\">"); 1031 Enumeration<?> list1 = req.getHeaderNames(); 1032 while (list1.hasMoreElements()) 1033 { 1034 String name = (String) list1.nextElement(); 1035 Enumeration<?> list2 = req.getHeaders(name); 1036 while (list2.hasMoreElements()) 1037 { 1038 String value = (String) list2.nextElement(); 1039 output.println("<tr><td>" + name + "</td><td>" + value + "</td></tr>"); 1040 } 1041 } 1042 output.println("</table>"); 1043 output.println("<h2>Parts of form</h2>"); 1044 output.println("<p>The following are the parts of the multipart form</p>"); 1045 output.println("<ul>"); 1046 while (keys.hasMoreElements()) 1047 { 1048 String name = keys.nextElement(); 1049 String mime = thisPage.getElement(name).getMime(); 1050 String fileName = thisPage.getElement(name).getFilename(); 1051 String encoding = thisPage.getElement(name).getTransferEncoding(); 1052 output.println("<li><p>" + name + "</p>"); 1053 if (fileName == null) 1054 { 1055 output.println("<p>Filename not specified</p>"); 1056 } 1057 else 1058 { 1059 output.println("<p>Filename is " + fileName + "</p>"); 1060 } 1061 if (mime == null) 1062 { 1063 output.println("<p>Content type not specified</p>"); 1064 mime = new String(); 1065 } 1066 else 1067 { 1068 output.println("<p>Content-type: " + mime + "</p>"); 1069 } 1070 if (encoding == null) 1071 { 1072 output.println("<p>Transfer encoding not specified</p>"); 1073 } 1074 else 1075 { 1076 output.println("<p>Content-transfer-encoding: " + encoding + "</p>"); 1077 } 1078 if (mime.toUpperCase().startsWith("TEXT")) 1079 { 1080 output.println("<p>" + StringHelpers.escapeHTML(thisPage.getElement(name).toString()) 1081 + "</p></li>"); 1082 } 1083 output.println("<p>Size of contents: " + Integer.toString(thisPage.getElement(name).getContentsSize())); 1084 if (fileName != null && targetDirectory != null) 1085 { 1086 try 1087 { 1088 boolean validEntry = true; 1089 File outputFile = null; 1090 if (targetDirectory.length() == 0 || fileName.length() == 0) 1091 { 1092 validEntry = false; 1093 } 1094 if (validEntry) 1095 { 1096 outputFile = new File (targetDirectory, fileName); 1097 output.println("<p>Name of file is " + StringHelpers.escapeHTML(outputFile.getCanonicalPath()) + "</p>"); 1098 } 1099 if (!validEntry) 1100 { ; } 1101 else if (outputFile == null) 1102 { 1103 output.println("<p>Unable to open output file</p>"); 1104 } 1105 else 1106 { 1107 FileOutputStream outputStream = new FileOutputStream(outputFile); 1108 byte transfer[] = thisPage.getElement(name).getContents(); 1109 if (transfer == null) 1110 { 1111 output.println("<p>Unable to get file contents</p>"); 1112 } 1113 else 1114 { 1115 outputStream.write(thisPage.getElement(name).getContents()); 1116 } 1117 outputStream.close(); 1118 } 1119 } 1120 catch (IOException e) 1121 { 1122 output.println("<p>Error while writing file</p>"); 1123 output.println(StringHelpers.escapeHTML("<p>" + e.getClass().getName() + " " + 1124 e.getMessage() + "</p>")); 1125 } 1126 } 1127 else 1128 { ; } 1129 } 1130 output.println("</ul>"); 1131 output.println("<h2>Messages</h2>"); 1132 output.println("<p>These messages are normally printed only if an error occurs during the processing "); 1133 output.println("of the HTTP transaction. They are included here to test the behavior of the servlet.</p><ol>"); 1134 Vector<String> messages = thisPage.getMessageList(); 1135 messages.trimToSize(); 1136 for (int i = 0; i < messages.size(); i++) 1137 { 1138 output.println("<li><p>" + StringHelpers.escapeHTML(messages.elementAt(i)) + "</p></li>"); 1139 } 1140 output.println("</ol>"); 1141 output.println("</body></html>"); 1142 thisPage.sendContents(); 1143 } 1144}