1 23 24 package org.apache.slide.webdav.method; 25 26 import java.io.BufferedInputStream ; 27 import java.io.IOException ; 28 import java.io.InputStream ; 29 import java.util.Date ; 30 import java.util.Enumeration ; 31 import java.util.StringTokenizer ; 32 import java.util.Vector ; 33 34 import javax.servlet.ServletOutputStream ; 35 import javax.servlet.http.HttpServletRequest ; 36 import javax.servlet.http.HttpServletResponse ; 37 38 import org.apache.slide.common.NamespaceAccessToken; 39 import org.apache.slide.common.ServiceAccessException; 40 import org.apache.slide.common.SlideException; 41 import org.apache.slide.content.NodeRevisionDescriptor; 42 import org.apache.slide.content.NodeRevisionDescriptors; 43 import org.apache.slide.content.RevisionContentNotFoundException; 44 import org.apache.slide.content.RevisionDescriptorNotFoundException; 45 import org.apache.slide.content.RevisionNotFoundException; 46 import org.apache.slide.event.EventDispatcher; 47 import org.apache.slide.security.AccessDeniedException; 48 import org.apache.slide.structure.LinkedObjectNotFoundException; 49 import org.apache.slide.structure.ObjectNotFoundException; 50 import org.apache.slide.util.Configuration; 51 import org.apache.slide.webdav.WebdavException; 52 import org.apache.slide.webdav.WebdavServletConfig; 53 import org.apache.slide.webdav.event.WebdavEvent; 54 import org.apache.slide.webdav.util.DeltavConstants; 55 import org.apache.slide.webdav.util.DirectoryIndexGenerator; 56 import org.apache.slide.webdav.util.LabeledRevisionNotFoundException; 57 import org.apache.slide.webdav.util.PreconditionViolationException; 58 import org.apache.slide.webdav.util.VersioningHelper; 59 import org.apache.slide.webdav.util.ViolatedPrecondition; 60 import org.apache.slide.webdav.util.WebdavStatus; 61 import org.apache.slide.webdav.util.WebdavUtils; 62 63 67 public class GetMethod extends AbstractWebdavMethod implements ReadMethod { 68 69 70 72 73 protected final int BUFFER_SIZE = 2048; 74 75 76 77 80 protected static final String mimeSeparation = "SLIDE_MIME_BOUNDARY"; 81 82 83 86 protected int input = 2048; 87 88 89 92 protected int output = 2048; 93 94 95 98 protected boolean printContent = true; 99 100 103 protected VersioningHelper vHelp = null; 104 105 106 108 109 112 protected String resourcePath; 113 114 115 117 118 124 public GetMethod(NamespaceAccessToken token, WebdavServletConfig config) { 125 super(token, config); 126 } 127 128 129 131 132 protected void displayDirectory() throws IOException { 133 String directoryBrowsing = config.getInitParameter( "directory-browsing" ); 134 if( "true".equalsIgnoreCase(directoryBrowsing) ) { 135 try { 136 DirectoryIndexGenerator directoryIndexGenerator = 137 new DirectoryIndexGenerator 138 (token, config); 139 directoryIndexGenerator.generate(req, resp, slideToken); 140 } catch (AccessDeniedException e) { 141 resp.sendError(WebdavStatus.SC_FORBIDDEN); 142 } catch (ObjectNotFoundException e) { 143 resp.sendError(WebdavStatus.SC_NOT_FOUND); 144 } catch (LinkedObjectNotFoundException e) { 145 resp.sendError(WebdavStatus.SC_NOT_FOUND); 146 } catch (SlideException e) { 147 resp.setStatus(WebdavStatus.SC_INTERNAL_SERVER_ERROR); 148 } 149 } else { 150 resp.sendError(WebdavStatus.SC_FORBIDDEN); 151 } 152 } 153 154 157 protected void parseRequest() 158 throws WebdavException { 159 vHelp = VersioningHelper.getVersioningHelper( 160 slideToken, token, req, resp, getConfig() ); 161 resourcePath = requestUri; 162 if (resourcePath == null) { 163 resourcePath = "/"; 164 } 165 166 if (Configuration.useVersionControl()) { 168 try { 169 170 String labelHeader = WebdavUtils.fixTomcatHeader(requestHeaders.getLabel(), "UTF-8"); 171 resourcePath = vHelp.getLabeledResourceUri(resourcePath, labelHeader); 172 } 173 catch (LabeledRevisionNotFoundException e) { 174 ViolatedPrecondition violatedPrecondition = 175 new ViolatedPrecondition(DeltavConstants.C_MUST_SELECT_VERSION_IN_HISTORY, 176 WebdavStatus.SC_CONFLICT); 177 try { 178 sendPreconditionViolation(new PreconditionViolationException(violatedPrecondition, 179 resourcePath)); 180 } catch (IOException ioe) {} 181 throw new WebdavException( WebdavStatus.SC_CONFLICT ); 182 } 183 catch (SlideException e) { 184 int statusCode = getErrorCode( (Exception )e ); 185 sendError( statusCode, e ); 186 throw new WebdavException( statusCode ); 187 } 188 } 189 190 } 191 192 193 198 protected void executeRequest() 199 throws WebdavException { 200 201 try { 203 if (isLockNull(resourcePath)) { 204 int statusCode = WebdavStatus.SC_NOT_FOUND; 205 sendError( statusCode, "lock-null resource", new Object []{resourcePath} ); 206 throw new WebdavException( statusCode ); 207 } 208 } 209 catch (ServiceAccessException e) { 210 int statusCode = getErrorCode((Exception )e); 211 sendError( statusCode, e ); 212 throw new WebdavException( statusCode ); 213 } 214 215 try { 216 if ( WebdavEvent.GET.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(WebdavEvent.GET, new WebdavEvent(this)); 218 219 221 structure.retrieve(slideToken, resourcePath); 222 NodeRevisionDescriptors revisionDescriptors = 223 content.retrieve(slideToken, resourcePath); 224 225 if (revisionDescriptors.hasRevisions()) { 226 227 NodeRevisionDescriptor revisionDescriptor = 229 content.retrieve(slideToken, revisionDescriptors); 230 231 if (revisionDescriptor != null) { 232 233 if (revisionDescriptor.propertyValueContains( 234 NodeRevisionDescriptor.RESOURCE_TYPE, 235 NodeRevisionDescriptor.COLLECTION_TYPE)) { 236 displayDirectory(); 237 } else { 238 239 ResourceInfo resourceInfo = 240 new ResourceInfo(resourcePath, revisionDescriptor); 241 242 if (!checkIfHeaders(req, resp, resourceInfo)) 244 return; 245 246 ServletOutputStream os = resp.getOutputStream(); 247 InputStream is = null; 248 249 if (printContent) { 250 is = content.retrieve 251 (slideToken, revisionDescriptors, 252 revisionDescriptor).streamContent(); 253 } 254 255 Vector ranges = parseRange(req, resp, resourceInfo); 256 257 resp.setHeader("ETag", getETag(resourceInfo, true) ); 259 resp.setHeader 260 ("Content-Language", revisionDescriptor.getContentLanguage()); 261 resp.addHeader 262 ("Last-Modified", 263 revisionDescriptor.getLastModified().toString()); 264 265 resp.setBufferSize(output); 268 269 if ( ((ranges == null) || (ranges.isEmpty())) ) { 270 resp.setContentType 272 (revisionDescriptor.getContentType()); 273 resp.setContentLength 274 ((int) revisionDescriptor.getContentLength()); 275 276 if (printContent) { 279 copy(resourceInfo, is, os); 280 } 281 282 } else { 283 285 resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); 286 287 if (ranges.size() == 1) { 288 289 Range range = (Range) ranges.elementAt(0); 290 resp.addHeader("Content-Range", "bytes " 291 + range.start 292 + "-" + range.end + "/" 293 + range.fileLength); 294 resp.setContentLength((int) range.length); 295 resp.setContentType 296 (revisionDescriptor.getContentType()); 297 298 if (printContent) { 299 copy(resourceInfo, is, os, range); 300 } 301 302 } else { 303 304 resp.setContentType 305 ("multipart/byteranges; boundary=" 306 + mimeSeparation); 307 308 if (printContent) { 309 copy(resourceInfo, is, os, 310 ranges.elements(), 311 revisionDescriptor.getContentType()); 312 } 313 } 314 } 315 } 316 } else { 317 displayDirectory(); 319 } 321 } else { 322 displayDirectory(); 324 } 326 } catch (Exception e) { 327 int statusCode; 328 if (e instanceof IOException ) { 332 statusCode = WebdavStatus.SC_PRECONDITION_FAILED; 335 } else { 336 statusCode = getErrorCode( e ); 337 } 338 sendError( statusCode, e ); 339 throw new WebdavException( statusCode ); 340 } 341 } 342 343 344 347 protected int getErrorCode(Exception ex) { 348 try { 349 throw ex; 350 } catch (RevisionNotFoundException e) { 351 return WebdavStatus.SC_NOT_FOUND; 352 } catch (RevisionContentNotFoundException e) { 353 return WebdavStatus.SC_NOT_FOUND; 354 } catch (RevisionDescriptorNotFoundException e) { 355 return WebdavStatus.SC_NOT_FOUND; 356 } catch (LinkedObjectNotFoundException e) { 357 return WebdavStatus.SC_NOT_FOUND; 358 } catch (Exception e) { 359 return super.getErrorCode(e); 360 } 361 } 362 363 364 365 367 368 369 370 380 private void copy(ResourceInfo resourceInfo, 381 InputStream resourceInputStream, 382 ServletOutputStream ostream) 383 throws IOException { 384 385 IOException exception = null; 386 387 InputStream istream = new BufferedInputStream 388 (resourceInputStream, input); 389 390 exception = copyRange(istream, ostream); 392 393 try { 395 istream.close(); 396 } catch (Throwable t) { 397 ; 398 } 399 400 try { 401 ostream.flush(); 402 } catch (Throwable t) { 403 ; 404 } 405 try { 406 ostream.close(); 407 } catch (Throwable t) { 408 ; 409 } 410 411 if (exception != null) 413 throw exception; 414 415 } 416 417 418 428 private void copy(ResourceInfo resourceInfo, 429 InputStream resourceInputStream, 430 ServletOutputStream ostream, 431 Range range) 432 throws IOException { 433 434 IOException exception = null; 435 436 InputStream istream = 437 new BufferedInputStream (resourceInputStream, input); 438 exception = copyRange(istream, ostream, range.start, range.end); 439 440 try { 442 istream.close(); 443 } catch (Throwable t) { 444 ; 445 } 446 try { 447 ostream.flush(); 448 } catch (Throwable t) { 449 ; 450 } 451 try { 452 ostream.close(); 453 } catch (Throwable t) { 454 ; 455 } 456 457 if (exception != null) 459 throw exception; 460 461 } 462 463 464 475 private void copy(ResourceInfo resourceInfo, 476 InputStream resourceInputStream, 477 ServletOutputStream ostream, 478 Enumeration ranges, String contentType) 479 throws IOException { 480 481 IOException exception = null; 482 483 while ( (exception == null) && (ranges.hasMoreElements()) ) { 484 485 InputStream istream = 486 new BufferedInputStream (resourceInputStream, input); 487 488 Range currentRange = (Range) ranges.nextElement(); 489 490 ostream.println("--" + mimeSeparation); 492 if (contentType != null) 493 ostream.println("Content-Type: " + contentType); 494 ostream.println("Content-Range: bytes " + currentRange.start 495 + "-" + currentRange.end + "/" 496 + currentRange.fileLength); 497 ostream.println(); 498 499 exception = copyRange(istream, ostream, currentRange.start, 501 currentRange.end); 502 503 try { 504 istream.close(); 505 } catch (Throwable t) { 506 ; 507 } 508 509 } 510 511 ostream.print("--" + mimeSeparation + "--"); 512 513 try { 515 ostream.flush(); 516 } catch (Throwable t) { 517 ; 518 } 519 try { 520 ostream.close(); 521 } catch (Throwable t) { 522 ; 523 } 524 525 if (exception != null) 527 throw exception; 528 529 } 530 531 532 541 private IOException copyRange(InputStream istream, 542 ServletOutputStream ostream) { 543 544 IOException exception = null; 546 byte buffer[] = new byte[input]; 547 int len = buffer.length; 548 while (true) { 549 try { 550 len = istream.read(buffer); 551 if (len == -1) 552 break; 553 ostream.write(buffer, 0, len); 554 } catch (IOException e) { 555 exception = e; 556 len = -1; 557 break; 558 } 559 } 560 return exception; 561 562 } 563 564 565 576 private IOException copyRange(InputStream istream, 577 ServletOutputStream ostream, 578 long start, long end) { 579 580 try { 581 istream.skip(start); 582 } catch (IOException e) { 583 return e; 584 } 585 586 IOException exception = null; 587 long bytesToRead = end - start + 1; 588 589 byte buffer[] = new byte[input]; 590 int len = buffer.length; 591 while ( (bytesToRead > 0) && (len >= buffer.length)) { 592 try { 593 len = istream.read(buffer); 594 if (bytesToRead >= len) { 595 ostream.write(buffer, 0, len); 596 bytesToRead -= len; 597 } else { 598 ostream.write(buffer, 0, (int) bytesToRead); 599 bytesToRead = 0; 600 } 601 } catch (IOException e) { 602 exception = e; 603 len = -1; 604 } 605 if (len < buffer.length) 606 break; 607 } 608 609 return exception; 610 611 } 612 613 614 622 private Vector parseRange(HttpServletRequest request, 623 HttpServletResponse response, 624 ResourceInfo resourceInfo) 625 throws IOException , WebdavException 626 { 627 628 String rangeHeader = request.getHeader("Range"); 630 if (rangeHeader == null) { 631 return null; 632 } 633 634 String headerValue = request.getHeader("If-Range"); 636 if (headerValue != null) { 637 headerValue = headerValue.trim(); 638 639 long lastModified = resourceInfo.date; 640 641 Date date = parseHttpDate(headerValue); 642 if (date == null) { 643 if (headerValue.startsWith("\"")) { 646 if (!getETag(resourceInfo, true).equals(headerValue)) { 647 return null; 648 } 649 } else { 650 if (!getETagValue(resourceInfo, true).equals(headerValue)) { 652 return null; 653 } 654 } 655 } else { 656 657 if (lastModified > (date.getTime() + 1000)) 661 return null; 662 } 663 } 664 665 long fileLength = resourceInfo.length; 666 667 if (fileLength == 0) return null; 668 669 if (!rangeHeader.startsWith("bytes")) { 672 response.sendError 673 (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 674 throw new WebdavException( 675 HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 676 } 677 rangeHeader = rangeHeader.substring(6); 678 679 680 Vector result = new Vector (); 683 StringTokenizer commaTokenizer = new StringTokenizer (rangeHeader, ","); 684 685 while (commaTokenizer.hasMoreTokens()) { 687 String rangeDefinition = commaTokenizer.nextToken(); 688 689 Range currentRange = new Range(); 690 currentRange.fileLength = fileLength; 691 692 int dashPos = rangeDefinition.indexOf('-'); 693 694 if (dashPos == -1) { 695 response.sendError 696 (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 697 throw new WebdavException( 698 HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 699 } 700 701 if (dashPos == 0) { 702 703 try { 704 long offset = Long.parseLong(rangeDefinition); 705 currentRange.start = fileLength + offset; 706 currentRange.end = fileLength - 1; 707 } catch (NumberFormatException e) { 708 response.sendError( 709 HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 710 throw new WebdavException( 711 HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 712 } 713 714 } else { 715 716 try { 717 currentRange.start = Long.parseLong 718 (rangeDefinition.substring(0, dashPos)); 719 if (dashPos < rangeDefinition.length() - 1) 720 currentRange.end = Long.parseLong 721 (rangeDefinition.substring 722 (dashPos + 1, rangeDefinition.length())); 723 else 724 currentRange.end = fileLength - 1; 725 } catch (NumberFormatException e) { 726 response.sendError( 727 HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 728 throw new WebdavException( 729 HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 730 } 731 732 } 733 734 currentRange.length = (currentRange.end - currentRange.start + 1); 735 if (!currentRange.validate()) { 736 response.sendError 737 (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 738 throw new WebdavException(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 739 } 740 741 result.addElement(currentRange); 742 } 743 744 return result; 745 } 746 747 748 750 751 private static class Range { 752 753 public long start; 754 public long end; 755 public long length; 756 public long fileLength; 757 758 761 public boolean validate() { 762 return ( (start >= 0) && (end >= 0) && (length > 0) 763 && (start <= end) && (end < fileLength) && (fileLength >= length)); 764 } 765 766 } 767 } 768 | Popular Tags |