1 23 24 package org.apache.slide.webdav.method; 25 26 import java.io.IOException ; 27 import java.io.PrintWriter ; 28 import java.io.StringWriter ; 29 import java.text.ParseException ; 30 import java.text.SimpleDateFormat ; 31 import java.util.ArrayList ; 32 import java.util.Collections ; 33 import java.util.Date ; 34 import java.util.Enumeration ; 35 import java.util.Iterator ; 36 import java.util.List ; 37 import java.util.Locale ; 38 import java.util.StringTokenizer ; 39 40 import javax.servlet.http.HttpServletRequest ; 41 import javax.servlet.http.HttpServletResponse ; 42 import javax.transaction.SystemException ; 43 import javax.transaction.Transaction ; 44 45 import org.apache.commons.transaction.locking.GenericLock; 46 import org.apache.commons.transaction.locking.MultiLevelLock; 47 import org.apache.commons.transaction.util.PrintWriterLogger; 48 49 import org.apache.slide.authenticate.CredentialsToken; 50 import org.apache.slide.common.Domain; 51 import org.apache.slide.common.NamespaceAccessToken; 52 import org.apache.slide.common.NestedSlideException; 53 import org.apache.slide.common.ServiceAccessException; 54 import org.apache.slide.common.SlideException; 55 import org.apache.slide.common.SlideToken; 56 import org.apache.slide.content.Content; 57 import org.apache.slide.content.NodeProperty; 58 import org.apache.slide.content.NodeRevisionContent; 59 import org.apache.slide.content.NodeRevisionDescriptor; 60 import org.apache.slide.content.NodeRevisionDescriptors; 61 import org.apache.slide.content.NodeProperty.NamespaceCache; 62 import org.apache.slide.lock.Lock; 63 import org.apache.slide.lock.NodeLock; 64 import org.apache.slide.macro.Macro; 65 import org.apache.slide.search.Search; 66 import org.apache.slide.security.Security; 67 import org.apache.slide.structure.ObjectNode; 68 import org.apache.slide.structure.ObjectNotFoundException; 69 import org.apache.slide.structure.Structure; 70 import org.apache.slide.transaction.ExternalTransactionContext; 71 import org.apache.slide.util.Messages; 72 import org.apache.slide.util.XMLValue; 73 import org.apache.slide.util.logger.Logger; 74 import org.apache.slide.webdav.WebdavException; 75 import org.apache.slide.webdav.WebdavMethod; 76 import org.apache.slide.webdav.WebdavServletConfig; 77 import org.apache.slide.webdav.util.BindConstants; 78 import org.apache.slide.webdav.util.DeltavConstants; 79 import org.apache.slide.webdav.util.NotificationConstants; 80 import org.apache.slide.webdav.util.PreconditionViolationException; 81 import org.apache.slide.webdav.util.TransactionConstants; 82 import org.apache.slide.webdav.util.UnlockListenerImpl; 83 import org.apache.slide.webdav.util.UriHandler; 84 import org.apache.slide.webdav.util.ViolatedPrecondition; 85 import org.apache.slide.webdav.util.WebdavConstants; 86 import org.apache.slide.webdav.util.WebdavStatus; 87 import org.apache.slide.webdav.util.WebdavUtils; 88 import org.jdom.Document; 89 import org.jdom.Element; 90 import org.jdom.JDOMException; 91 import org.jdom.Namespace; 92 import org.jdom.input.SAXBuilder; 93 import org.jdom.output.XMLOutputter; 94 95 99 public abstract class AbstractWebdavMethod 100 implements WebdavMethod, WebdavConstants, DeltavConstants, BindConstants, NotificationConstants, TransactionConstants { 101 102 103 105 108 protected static final String NO_CACHE = "no-cache"; 109 110 113 public static final String HTTP_PROTOCOL = "http://"; 114 115 118 public static final String HTTP_VERSION = "HTTP/1.1"; 119 120 123 public static final String TEXT_XML = "text/xml"; 124 125 128 public static final String TEXT_XML_UTF_8 = "text/xml; charset=\"UTF-8\""; 129 130 133 public static final String XML_RESPONSE_INDENT = " "; 134 135 private static final String LOG_CHANNEL = 136 AbstractWebdavMethod.class.getName(); 137 138 141 public static final int INFINITY = Integer.MAX_VALUE; 142 143 protected static final Namespace DNSP = NamespaceCache.DEFAULT_NAMESPACE; 144 145 148 protected static final SimpleDateFormat formats[] = { 149 new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), 150 new SimpleDateFormat ("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), 151 new SimpleDateFormat ("EEE MMMM d HH:mm:ss yyyy", Locale.US) 152 }; 153 154 private static final MultiLevelLock GLOBAL_LOCK = new GenericLock("global", 2, new PrintWriterLogger( 156 new PrintWriter (System.out), LOG_CHANNEL, false)); 157 158 160 161 164 protected String requestUri; 165 166 167 170 protected HttpServletRequest req; 171 172 173 176 protected HttpServletResponse resp; 177 178 179 182 protected WebdavServletConfig config; 183 184 protected String slideContextPath; 185 186 187 190 protected String requestBody; 191 192 193 196 protected NamespaceAccessToken token; 197 198 199 202 protected Structure structure; 203 204 205 208 protected Content content; 209 210 211 214 protected Security security; 215 216 217 220 protected Lock lock; 221 222 223 226 protected Search search; 227 228 229 232 protected Macro macro; 233 234 235 238 protected SlideToken slideToken; 239 240 243 private Document requestContentDocument = null; 244 245 248 private boolean isRequestContentParsed = false; 249 250 253 protected RequestHeaders requestHeaders = new RequestHeaders(); 254 255 257 258 269 270 272 273 279 public AbstractWebdavMethod(NamespaceAccessToken token, 280 WebdavServletConfig config) { 281 282 this.config = config; 283 this.token = token; 284 285 structure = token.getStructureHelper(); 287 content = token.getContentHelper(); 288 security = token.getSecurityHelper(); 289 lock = token.getLockHelper(); 290 macro = token.getMacroHelper(); 291 } 292 293 294 296 297 302 public void run(HttpServletRequest req, HttpServletResponse resp) 303 throws WebdavException { 304 305 UriHandler.setGloballyUseHistoryCollectionHack(useHistoryCollectionHack()); 308 309 this.req = req; 310 this.resp = resp; 311 this.slideToken = WebdavUtils.getSlideToken(req); 312 String forceLowercaseLogin = token.getNamespaceConfig().getParameter("force-lowercase-login"); 313 if ("true".equals(forceLowercaseLogin)) { 314 String name = slideToken.getCredentialsToken().getPrincipal().getName().toLowerCase(); 315 slideToken.setCredentialsToken(new CredentialsToken(name)); 316 } 317 318 this.requestUri = WebdavUtils.getRelativePath(req, config); 319 this.slideContextPath = req.getContextPath(); 320 if (!this.config.isDefaultServlet()) { 321 this.slideContextPath += req.getServletPath(); 322 } 323 324 slideToken.addParameter("slideContextPath", this.slideContextPath); 327 328 parseRequestHeaders(); 329 330 boolean transactionIsStarted = false; 331 boolean globalLockObtained = false; 332 String txId = null; 333 try { 334 parseRequest(); 335 336 ExternalTransactionContext externalTransaction = null; 337 338 txId = requestHeaders.getTxId(); 339 if (txId != null) { 340 externalTransaction = ExternalTransactionContext.lookupContext(txId); 341 if (externalTransaction != null) { 342 Domain.log("Using external transaction " + txId, LOG_CHANNEL, Logger.INFO); 343 slideToken.setExternalTx(); 344 slideToken.setForceStoreEnlistment(true); 346 Transaction tx = externalTransaction.getTransaction(); 347 token.getTransactionManager().resume(tx); 348 transactionIsStarted = true; 349 } 350 } 351 352 if (!slideToken.isExternalTransaction()) { 353 token.begin(); 354 transactionIsStarted = true; 355 if (txForAllRequests()) { 356 slideToken.setForceStoreEnlistment(true); 357 } 358 359 if (this instanceof ReadMethod) { 360 assureGlobalReadLock(); 361 } else if (this instanceof WriteMethod) { 362 assureGlobalWriteLock(); 363 } 364 globalLockObtained = true; 365 } 366 367 boolean isEndofTransactionxUnlock = false; 369 if (this instanceof UnlockMethod) 370 { 371 UnlockMethod meth = (UnlockMethod)this; 372 if (meth.getCommand() != UnlockMethod.NO_TRANSACTION) 373 isEndofTransactionxUnlock = true; 374 } 375 376 383 if (!isEndofTransactionxUnlock) 384 { 385 try { 386 structure.retrieve(slideToken, requestUri); 388 UnlockListenerImpl listener = new UnlockListenerImpl(slideToken, token, config, req, resp); 390 lock.clearExpiredLocks(slideToken, requestUri, listener); 391 392 if (listener.getUnlockCount() > 0) { 393 token.commit(); 398 token.begin(); 399 } 400 } catch (ObjectNotFoundException e) { 401 } 403 } 404 405 executeRequest(); 406 407 if (!slideToken.isExternalTransaction() && transactionIsStarted) { 408 token.commit(); 409 transactionIsStarted = false; 410 } 411 } catch (WebdavException ex) { 412 } catch (SlideException ex) { 415 int statusCode = getErrorCode( ex ); 416 sendError( statusCode, ex ); 417 } catch (Exception ex) { 421 token.getLogger().log(ex,LOG_CHANNEL,Logger.ERROR); 422 int statusCode = WebdavStatus.SC_INTERNAL_SERVER_ERROR; 423 sendError( statusCode, ex ); 424 throw new WebdavException( statusCode ); 425 } finally { 426 if (!slideToken.isExternalTransaction() && transactionIsStarted) { 427 try { 429 token.rollback(); 430 } catch (Exception e) { 431 e.printStackTrace(); 433 } 434 } 435 if (slideToken.isExternalTransaction()) { 436 Transaction transaction; 437 try { 438 if (token.getStatus() == javax.transaction.Status.STATUS_ACTIVE) { 439 transaction = token.getTransactionManager().suspend(); 440 if (transaction != null) { 441 ExternalTransactionContext.registerContext(txId, transaction); 442 } 443 } 444 } catch (SystemException e) { 445 e.printStackTrace(); 447 } 448 } 449 if (globalLockObtained) { 450 releaseGlobalLock(); 451 } 452 } 453 } 454 455 457 458 463 public WebdavServletConfig getConfig() { 464 465 return config; 466 } 467 468 469 473 public String getFullPath(String slidePath) { 474 return WebdavUtils.getAbsolutePath(slidePath, req, getConfig()); 475 } 476 477 478 482 public String getSlidePath(String fullpath) { 483 return WebdavUtils.getSlidePath(fullpath, getSlideContextPath()); 484 } 485 486 public String getSlideContextPath() { 487 return this.slideContextPath; 488 } 489 490 492 498 protected void readRequestContent() { 499 500 if (req.getContentLength() == 0) 501 return; 502 503 505 try { 506 requestBody = new String (NodeRevisionContent.read(req.getInputStream()), 507 getEncodingString(req.getCharacterEncoding())); 508 } 509 catch (Exception e) { 510 token.getLogger().log(e,LOG_CHANNEL,Logger.ERROR); 511 } 512 } 513 514 515 516 519 public static String getEncodingString(String httpEncoding) { 520 String result = httpEncoding; 521 if (result == null) result = System.getProperty("file.encoding"); 522 if (result.startsWith("\"")) result = result.substring(1, result.length()); 523 if (result.endsWith("\"")) result = result.substring(0, result.length()-1); 524 return result; 525 } 526 527 528 532 private void parseRequestHeaders() throws WebdavException { 533 requestHeaders.parse(); 534 } 535 536 539 protected boolean isCollection(String path) { 540 return WebdavUtils.isCollection(token, slideToken, path); 541 } 542 543 554 protected boolean isDescendant( ObjectNode lowerNode, ObjectNode upperNode ) throws ServiceAccessException { 555 if (lowerNode.getUuri().equals(upperNode.getUuri())) { 556 return true; 557 } 558 if (upperNode.hasBinding(lowerNode)) { 559 return true; 560 } 561 562 NodeRevisionDescriptors lowerNrds = null; 563 NodeRevisionDescriptor lowerNrd = null; 564 try { 565 lowerNrds = content.retrieve(slideToken, lowerNode.getUri()); 566 lowerNrd = content.retrieve(slideToken, lowerNrds); 567 568 NodeProperty psProp = lowerNrd.getProperty(P_PARENT_SET); 569 XMLValue xv = new XMLValue( String.valueOf(psProp.getValue()) ); 570 Iterator i = xv.getList().iterator(); 571 while (i.hasNext()) { 572 Element pElm = (Element)i.next(); 574 String hrPath = pElm.getChild( E_HREF, DNSP ).getText(); 575 ObjectNode hrNode = structure.retrieve( slideToken, hrPath ); 576 return isDescendant( hrNode, upperNode ); 577 } 578 } catch (ServiceAccessException e) { 579 throw e; 580 } catch (Exception e) {} 581 582 return false; 583 } 584 585 protected boolean isRequestChunked() { 586 String te = req.getHeader("Transfer-Encoding"); 587 if (te == null) return false; 588 return te.indexOf("chunked") != -1; 589 } 590 591 596 protected abstract void parseRequest() 597 throws WebdavException; 598 599 600 601 606 protected Document getRequestContent() { 607 return requestContentDocument; 608 } 609 610 612 615 protected String parseUri(String uri) throws WebdavException { int protocolIndex = uri.indexOf("://"); 617 if (protocolIndex >= 0) { 618 int firstSeparator = 621 uri.indexOf("/", protocolIndex + 4); 622 if (firstSeparator < 0) { 623 uri = "/"; 624 } else { 625 uri = uri.substring(firstSeparator); 626 } 627 } else { 628 String hostName = req.getServerName(); 629 if ((hostName != null) && (uri.startsWith(hostName))) { 630 uri = uri.substring(hostName.length()); 631 } 632 633 int portIndex = uri.indexOf(":"); 634 if (portIndex >= 0) { 635 uri = uri.substring(portIndex); 636 } 637 638 if (uri.startsWith(":")) { 639 int firstSeparator = uri.indexOf("/"); 640 if (firstSeparator < 0) { 641 uri = "/"; 642 } else { 643 uri = uri.substring(firstSeparator); 644 } 645 } 646 } 647 648 uri = WebdavUtils.decodeURL(uri); 651 652 String contextPath = req.getContextPath(); 653 if ((contextPath != null) && (uri.startsWith(contextPath))) { 654 uri = uri.substring(contextPath.length()); 655 } 656 657 String pathInfo = req.getPathInfo(); 658 if (pathInfo != null) { 659 String servletPath = req.getServletPath(); 660 if ((servletPath != null) && (uri.startsWith(servletPath))) { 661 uri = uri.substring(servletPath.length()); 662 } 663 } 664 uri = getConfig().getScope() + uri; 665 666 return uri; 667 } 668 669 protected Element parseRequestContent(String rootName) throws JDOMException, IOException { 670 Document document; 671 Element root; 672 673 document = parseRequestContent(); 674 if (document == null) { 675 throw new JDOMException("Request content missing"); 676 } 677 root = document.getRootElement(); 678 if( root == null || !root.getName().equals(rootName) ) { 679 Domain.warn( "Root element must be "+rootName ); 680 throw new JDOMException("Root element must be <"+rootName+">"); 681 } 682 return root; 683 } 684 685 693 protected Document parseRequestContent() throws JDOMException, IOException { 694 695 if (isRequestContentParsed) { 696 return requestContentDocument; 697 } 698 699 if (req.getContentLength() == 0 || req.getContentLength() == -1) { 700 return requestContentDocument; 701 } 702 703 try { 704 requestContentDocument = new SAXBuilder().build(req.getInputStream()); 705 isRequestContentParsed = true; 706 } 707 catch (JDOMException e) { 708 if (e.getCause() instanceof IOException ) { 709 throw (IOException )e.getCause(); 710 } 711 else { 712 throw e; 713 } 714 } 715 716 return requestContentDocument; 717 } 718 719 720 725 protected abstract void executeRequest() 726 throws WebdavException, IOException ; 727 728 729 734 protected boolean isMsProprietarySupport() { 735 return (token.getNamespaceConfig().getParameter("ms") != null); 736 } 737 738 744 protected void sendPreconditionViolation(PreconditionViolationException pve) throws IOException { 745 746 if (pve != null) { 747 748 ViolatedPrecondition violatedPrecondition = pve.getViolatedPrecondition(); 749 750 int statusCode = violatedPrecondition.getStatusCode(); 751 printStackTrace( pve, statusCode ); 752 String statusText = WebdavStatus.getStatusText(statusCode); 753 if (violatedPrecondition.getExplanation() != null) { 754 statusText = statusText+": "+violatedPrecondition.getExplanation(); 755 } 756 resp.setStatus(statusCode, statusText); 757 758 resp.setContentType(TEXT_XML_UTF_8); 759 760 org.jdom.output.Format format = org.jdom.output.Format.getPrettyFormat(); 761 format.setIndent(XML_RESPONSE_INDENT); 762 new XMLOutputter(format). 763 output(new Document(MethodUtil.getPreconditionViolationError(pve.getViolatedPrecondition())), resp.getWriter()); 764 } 765 } 766 767 769 770 773 protected int getErrorCode(Throwable ex) { 774 return WebdavUtils.getErrorCode(ex); 775 } 776 777 778 781 protected int getErrorCode(SlideException ex) { 782 return WebdavUtils.getErrorCode(ex); 783 } 784 785 786 787 790 protected int getErrorCode(ServiceAccessException ex) { 791 return WebdavUtils.getErrorCode(ex); 792 } 793 794 795 799 protected boolean getBooleanInitParameter( String name ) { 800 return "true".equalsIgnoreCase( getConfig().getInitParameter(name) ); 801 } 802 803 807 protected boolean useHistoryCollectionHack() { 808 return "true".equalsIgnoreCase(token.getNamespaceConfig().getParameter("history-collection-hack")); 809 } 810 811 814 protected boolean txForAllRequests() { 815 return "true".equalsIgnoreCase(token.getNamespaceConfig().getParameter("all-methods-in-transactions")); 816 } 817 818 822 protected boolean isSequentialWrite() { 823 String sm = token.getNamespaceConfig().getParameter("sequential-mode"); 824 return ("write".equalsIgnoreCase(sm) || "full".equalsIgnoreCase(sm)); 825 } 826 827 831 protected boolean isSequentialRead() { 832 return "full".equalsIgnoreCase(token.getNamespaceConfig().getParameter("sequential-mode")); 833 } 834 835 protected void assureGlobalReadLock() { 836 if (isSequentialRead()) { 837 try { 838 GLOBAL_LOCK.acquire(this, 1, true, true, Long.MAX_VALUE); 839 } catch (InterruptedException e) { 840 } 841 } 842 } 843 844 protected void assureGlobalWriteLock() { 845 if (isSequentialWrite()) { 846 try { 847 GLOBAL_LOCK.acquire(this, 2, true, true, Long.MAX_VALUE); 848 } catch (InterruptedException e) { 849 } 850 } 851 } 852 853 protected void releaseGlobalLock() { 854 GLOBAL_LOCK.release(this); 855 } 856 857 861 protected int getIntInitParameter( String name ) { 862 int result = -1; 863 try { 864 result = Integer.parseInt( getConfig().getInitParameter(name) ); 865 } 866 catch( NumberFormatException x ) {}; 867 return result; 868 } 869 870 873 protected void sendError( int statusCode ) { 874 try { resp.sendError( statusCode ); } catch( Throwable x ) {}; 875 } 876 877 880 protected void sendError( int statusCode, String message ) { 881 String statusText = 882 WebdavStatus.getStatusText(statusCode)+ 883 (message != null 884 ? ": "+Messages.format( message, (Object )null ) 885 : ""); 886 try { resp.sendError( statusCode, statusText ); } catch( Throwable x ) {}; 887 } 888 889 892 protected void sendError( int statusCode, String message, Object [] args ) { 893 String statusText = 894 WebdavStatus.getStatusText(statusCode)+": "+ 895 Messages.format( message, args ); 896 try { resp.sendError( statusCode, statusText ); } catch( Throwable x ) {}; 897 } 898 899 902 protected void sendError( int statusCode, Throwable t ) { 903 printStackTrace( t, statusCode ); 904 String explanation = (t == null || t.getMessage() == null || "".equals(t.getMessage()) 905 ? Messages.format(t.getClass().getName(), (Object )null) 906 : t.getMessage() 907 ); 908 String statusText = 909 WebdavStatus.getStatusText(statusCode)+": "+explanation; 910 try { resp.sendError( statusCode, statusText ); } catch( Throwable x ) {}; 911 } 912 913 919 protected void printStackTrace( Throwable x, int statusCode ) { 920 int printStackTraceFrom = getIntInitParameter( "printStackTrace" ); 921 if( printStackTraceFrom < 0 ) 922 printStackTraceFrom = 500; 923 if( statusCode >= printStackTraceFrom ) 924 x.printStackTrace(); 925 } 926 927 928 935 protected void generateStatusText(Element parentElement, String href, int statusCode) { 936 937 Element hrefElement = new Element(E_HREF, DNSP); 938 parentElement.addContent(hrefElement); 939 hrefElement.setText(getFullPath(href)); 940 Element statusElement = new Element(E_STATUS, DNSP); 941 parentElement.addContent(statusElement); 942 statusElement.setText("HTTP/1.1 " + statusCode + " " 943 + WebdavStatus.getStatusText(statusCode)); 944 } 945 946 952 protected String generateErrorMessage 953 (NestedSlideException nestedException) { 954 955 Element multistatus = new Element(E_MULTISTATUS, DNSP); 956 957 Enumeration nestedExceptionsList = 958 nestedException.enumerateExceptions(); 959 while (nestedExceptionsList.hasMoreElements()) { 960 961 Element response = new Element(E_RESPONSE, DNSP); 962 multistatus.addContent(response); 963 SlideException ex = 964 (SlideException) nestedExceptionsList.nextElement(); 965 generateStatusText(response, MethodUtil.getErrorMessage(ex), 966 getErrorCode(ex)); 967 if (ex instanceof PreconditionViolationException) { 968 response.addContent(MethodUtil.getPreconditionViolationResponseDescription((PreconditionViolationException)ex)); 969 } 970 971 } 972 973 StringWriter stringWriter = new StringWriter (); 974 try { 975 new XMLOutputter().output(multistatus, stringWriter); 976 } 977 catch (IOException e) { 978 Domain.log(e); 979 } 980 return stringWriter.toString(); 981 982 } 983 984 protected boolean exists( String uriStr ) throws SlideException { 985 boolean destinationExists = true; 986 987 try { 988 content.retrieve(slideToken, uriStr); 989 } 990 catch (ObjectNotFoundException x) { 991 destinationExists = false; 992 } 993 return destinationExists; 994 } 995 996 protected boolean isLocked( String uriStr ) throws ServiceAccessException { 997 boolean isLocked = false; 999 try { 1000 Enumeration locks = lock.enumerateLocks (slideToken, uriStr, false); 1001 while (locks.hasMoreElements()) { 1002 if (lock.isLocked(slideToken,(NodeLock) locks.nextElement(),false)) { 1003 isLocked = true; 1004 } 1005 } 1006 } 1007 catch (ServiceAccessException x) { 1008 throw x; 1009 } 1010 catch (SlideException x) { 1011 } 1013 return isLocked; 1014 } 1015 1016 protected boolean isLockNull( String uriStr ) throws ServiceAccessException { 1017 boolean isLockNull = false; 1018 try { 1019 NodeRevisionDescriptor nrd = 1020 content.retrieve(slideToken, content.retrieve(slideToken, uriStr)); 1021 isLockNull = isLockNull( nrd ); 1022 } 1023 catch (ServiceAccessException x) { 1024 throw x; 1025 } 1026 catch (SlideException x) { 1027 } 1029 return isLockNull; 1030 } 1031 1032 protected boolean isLockNull( NodeRevisionDescriptor nrd ) { 1033 return nrd.propertyValueContains(P_RESOURCETYPE, E_LOCKNULL); 1034 } 1035 1036 protected boolean isAutoVersionControl(String resourcePath) { 1037 return new Boolean (Domain.getParameter(I_AUTO_VERSION_CONTROL, 1038 I_AUTO_VERSION_CONTROL_DEFAULT, 1039 token.getUri(slideToken, resourcePath).getStore())) 1040 .booleanValue(); 1041 } 1042 1043 protected boolean isExcludedForVersionControl(String resourcePath) { 1044 String versionControlExcludePaths = 1045 Domain.getParameter(I_VERSIONCONTROL_EXCLUDEPATH, 1046 I_VERSIONCONTROL_EXCLUDEPATH_DEFAULT, 1047 token.getUri(slideToken, resourcePath).getStore()); 1048 if (versionControlExcludePaths != null && versionControlExcludePaths.length() > 0) { 1049 StringTokenizer st = new StringTokenizer (versionControlExcludePaths, ";"); 1050 while (st.hasMoreTokens()) { 1051 if (isExcluded(resourcePath, st.nextToken())) { 1052 return true; 1053 } 1054 } 1055 } 1056 return false; 1057 } 1058 1059 private boolean isExcluded(String resourcePath, String excludePath) { 1060 UriHandler uh = UriHandler.getUriHandler(resourcePath); 1061 if (excludePath != null && excludePath.length() > 0) { 1062 UriHandler exUh = UriHandler.getUriHandler(excludePath); 1063 if (exUh.isAncestorOf(uh)) { 1064 return true; 1065 } 1066 } 1067 return false; 1068 } 1069 1070 1081 protected boolean checkIfHeaders(HttpServletRequest request, 1082 HttpServletResponse response, 1083 ResourceInfo resourceInfo) 1084 throws IOException 1085 { 1086 String eTag = getETagValue(resourceInfo, true); 1089 long lastModified = resourceInfo.date; 1090 1091 StringTokenizer commaTokenizer; 1092 1093 String headerValue; 1094 1095 headerValue = request.getHeader("If-Match"); 1097 if (headerValue != null) { 1098 if (headerValue.indexOf("*") == -1) { 1099 1100 commaTokenizer = new StringTokenizer (headerValue, ", \""); 1101 boolean matchingTagFound = false; 1102 1103 while (!matchingTagFound && commaTokenizer.hasMoreTokens()) { 1104 matchingTagFound = commaTokenizer.nextToken().equals(eTag); 1105 } 1106 1107 if (!matchingTagFound) { 1110 response.sendError( 1111 HttpServletResponse.SC_PRECONDITION_FAILED); 1112 return false; 1113 } 1114 } else { 1115 if (!resourceInfo.exists()) { 1116 response.sendError( 1117 HttpServletResponse.SC_PRECONDITION_FAILED); 1118 return false; 1119 } 1120 } 1121 } 1122 1123 headerValue = request.getHeader("If-Modified-Since"); 1125 if (headerValue != null) { 1126 1127 if (request.getHeader("If-None-Match") == null) { 1130 Date date = parseHttpDate(headerValue); 1131 1132 if ((date != null) 1133 && (lastModified <= (date.getTime() + 1000)) ) { 1134 response.sendError 1137 (HttpServletResponse.SC_NOT_MODIFIED); 1138 return false; 1139 } 1140 } 1141 } 1142 1143 headerValue = request.getHeader("If-None-Match"); 1145 if (headerValue != null) { 1146 if (headerValue.indexOf("*") == -1) { 1147 1148 commaTokenizer = new StringTokenizer (headerValue, ", \""); 1149 while (commaTokenizer.hasMoreTokens()) { 1150 if (commaTokenizer.nextToken().equals(eTag)) { 1151 if ( ("GET".equals(request.getMethod())) 1154 || ("HEAD".equals(request.getMethod())) ) 1155 { 1156 response.sendError( 1157 HttpServletResponse.SC_NOT_MODIFIED); 1158 return false; 1159 } else { 1160 response.sendError( 1161 HttpServletResponse.SC_PRECONDITION_FAILED); 1162 return false; 1163 } 1164 } 1165 } 1166 } else { 1167 if (resourceInfo.exists()) { 1168 response.sendError( 1169 HttpServletResponse.SC_PRECONDITION_FAILED); 1170 return false; 1171 } 1172 } 1173 } 1174 1175 headerValue = request.getHeader("If-Unmodified-Since"); 1177 if (headerValue != null) { 1178 Date date = parseHttpDate(headerValue); 1179 1180 if ( (date != null) && (lastModified > date.getTime()) ) { 1181 response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); 1184 return false; 1185 } 1186 } 1187 return true; 1188 } 1189 1190 1198 protected Date parseHttpDate(String headerValue) { 1199 Date date = null; 1200 1201 for (int i = 0; (date == null) && (i < formats.length); i++) { 1203 try { 1204 synchronized (formats[i]) { 1205 date = formats[i].parse(headerValue); 1206 } 1207 } catch (ParseException e) { 1208 } 1210 } 1211 return date; 1212 } 1213 1214 1215 1222 protected String getETagValue(ResourceInfo resourceInfo, boolean strong) { 1223 if (resourceInfo.exists()) { 1226 return resourceInfo.etag; 1227 } 1228 else { 1229 return resourceInfo.length + "-" + resourceInfo.date; 1230 } 1231 } 1232 1233 1234 1241 protected String getETag(ResourceInfo resourceInfo, boolean strong) { 1242 if (strong) 1243 return "\"" + getETagValue(resourceInfo, strong) + "\""; 1244 else 1245 return "W/\"" + getETagValue(resourceInfo, strong) + "\""; 1246 } 1247 1248 protected class ResourceInfo { 1249 1250 1255 public ResourceInfo(String path, NodeRevisionDescriptor properties) { 1256 1257 this.path = path; 1258 this.exists = true; 1259 this.creationDate = properties.getCreationDateAsDate().getTime(); 1260 this.date = properties.getLastModifiedAsDate().getTime(); 1261 this.httpDate = properties.getLastModified(); 1262 this.length = properties.getContentLength(); 1263 this.etag = properties.getETag(); 1264 1265 } 1266 1270 public ResourceInfo(String path) { 1271 this.path = path; 1272 this.exists = false; 1273 this.length = 0; 1274 this.date = System.currentTimeMillis(); 1275 } 1276 1277 1278 public String path; 1279 public long creationDate; 1280 public String httpDate; 1281 public long date; 1282 public long length; 1283 public String etag; 1284 public boolean exists; 1286 1287 1288 1291 public boolean exists() { 1292 return exists; 1293 } 1294 1295 1296 1299 public String toString() { 1300 return path; 1301 } 1302 1303 1304 } 1305 1306 protected class RequestHeaders { 1307 private static final int 1308 ST_UNDEFINED = 0, 1309 ST_INVALID = 1, 1310 ST_DEFINED = 2; 1311 1312 private String hIfStr; 1314 private String hLockTokenStr; 1315 private String hDepthStr; 1316 private String hDestinationStr; 1317 private String hOverwriteStr; 1318 private String hTimeoutStr; 1319 private String hLabelStr; 1320 private String hNotificationTypeStr; 1321 private String hCallbackStr; 1322 private String hSubscriptionIdStr; 1323 private String hNotificationDelayStr; 1324 private String hSubscriptionLifetimeStr; 1325 private String hTxIdStr; 1326 private String hTxMethodStr; 1327 private String hContentTypeStr; 1328 1329 private List hIf; 1331 private String hLockToken; 1332 private int hDepth; 1333 private String hDestination; 1334 private boolean hOverwrite; 1335 private int hTimeout; 1336 private String hLabel; 1337 private String hNotificationType; 1338 private String hCallback; 1339 private int []hSubscriptionId; 1340 private int hNotificationDelay; 1341 private int hSubscriptionLifetime; 1342 private String hTxId; 1343 private String hTxMethod; 1344 private String hContentType; 1345 1346 private int stIf = ST_UNDEFINED; 1348 private int stLockToken = ST_UNDEFINED; 1349 private int stDepth = ST_UNDEFINED; 1350 private int stDestination = ST_UNDEFINED; 1351 private int stOverwrite = ST_UNDEFINED; 1352 private int stTimeout = ST_UNDEFINED; 1353 private int stLabel = ST_UNDEFINED; 1354 private int stNotificationType = ST_UNDEFINED; 1355 private int stCallback = ST_UNDEFINED; 1356 private int stSubscriptionId = ST_UNDEFINED; 1357 private int stNotificationDelay = ST_UNDEFINED; 1358 private int stSubscriptionLifetime = ST_UNDEFINED; 1359 private int stTxId = ST_UNDEFINED; 1360 private int stTxMethod= ST_UNDEFINED; 1361 private int stContentType = ST_UNDEFINED; 1362 1363 protected RequestHeaders() { 1364 } 1365 1366 protected boolean isDefined( String header ) { 1367 return req.getHeader(header) != null; 1368 } 1369 1370 protected List getIf() throws WebdavException { 1371 if (stIf == ST_UNDEFINED) { 1372 return Collections.EMPTY_LIST; 1373 } 1374 else if (stIf == ST_INVALID) { 1375 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1376 sendError( sc, "Invalid header If: "+hIfStr ); 1377 throw new WebdavException( sc ); 1378 } 1379 else { 1380 return hIf; 1381 } 1382 } 1383 1384 protected String getLockToken() throws WebdavException { 1385 if (stLockToken == ST_UNDEFINED) { 1386 return null; 1387 } 1388 else if (stLockToken == ST_INVALID) { 1389 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1390 sendError( sc, "Invalid header LockToken: "+hLockTokenStr ); 1391 throw new WebdavException( sc ); 1392 } 1393 else { 1394 return hLockToken; 1395 } 1396 } 1397 1398 protected int getDepth( int defaultValue ) throws WebdavException { 1399 if (stDepth == ST_UNDEFINED) { 1400 return defaultValue; 1401 } 1402 else if (stDepth == ST_INVALID) { 1403 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1404 sendError( sc, "Invalid header Depth: "+hDepthStr ); 1405 throw new WebdavException( sc ); 1406 } 1407 else { 1408 return hDepth; 1409 } 1410 } 1411 1412 protected String getDestination() throws WebdavException { 1413 if (stDestination == ST_UNDEFINED) { 1414 return null; 1415 } 1416 else if (stDestination == ST_INVALID) { 1417 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1418 sendError( sc, "Invalid header Destination: "+hDestinationStr ); 1419 throw new WebdavException( sc ); 1420 } 1421 else { 1422 return hDestination; 1423 } 1424 } 1425 1426 protected boolean getOverwrite( boolean defaultValue ) throws WebdavException { 1427 if (stOverwrite == ST_UNDEFINED) { 1428 return defaultValue; 1429 } 1430 else if (stOverwrite == ST_INVALID) { 1431 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1432 sendError( sc, "Invalid header Overwrite: "+hOverwriteStr ); 1433 throw new WebdavException( sc ); 1434 } 1435 else { 1436 return hOverwrite; 1437 } 1438 } 1439 1440 protected String getLabel() throws WebdavException { 1441 if (stLabel == ST_UNDEFINED) { 1442 return null; 1443 } 1444 else if (stLabel == ST_INVALID) { 1445 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1446 sendError( sc, "Invalid header Label: "+hLabelStr ); 1447 throw new WebdavException( sc ); 1448 } 1449 else { 1450 return hLabel; 1451 } 1452 } 1453 1454 protected int getTimeout( int defaultValue) throws WebdavException { 1455 if (stTimeout == ST_UNDEFINED) { 1456 return defaultValue; 1457 } 1458 else if (stTimeout == ST_INVALID) { 1459 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1460 sendError( sc, "Invalid header Timeout: "+hTimeoutStr ); 1461 throw new WebdavException( sc ); 1462 } 1463 else { 1464 return hTimeout; 1465 } 1466 } 1467 1468 protected String getNotificationType() throws WebdavException { 1469 if (stNotificationType == ST_UNDEFINED) { 1470 return null; 1471 } else if (stNotificationType == ST_INVALID) { 1472 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1473 sendError( sc, "Invalid notification type: "+hNotificationTypeStr ); 1474 throw new WebdavException( sc ); 1475 } 1476 else { 1477 return hNotificationType; 1478 } 1479 } 1480 1481 protected int []getSubscriptionId() throws WebdavException { 1482 if (stSubscriptionId == ST_UNDEFINED) { 1483 return new int[0]; 1484 } else if (stSubscriptionId == ST_INVALID) { 1485 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1486 sendError( sc, "Invalid subscription ID: "+hSubscriptionIdStr ); 1487 throw new WebdavException( sc ); 1488 } 1489 else { 1490 return hSubscriptionId; 1491 } 1492 } 1493 1494 protected String getCallback() throws WebdavException { 1495 if (stCallback == ST_UNDEFINED) { 1496 return null; 1497 } else if (stCallback == ST_INVALID) { 1498 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1499 sendError( sc, "Invalid callback: "+hCallbackStr ); 1500 throw new WebdavException( sc ); 1501 } 1502 else { 1503 return hCallback; 1504 } 1505 } 1506 1507 protected int getNotificationDelay( int defaultValue ) throws WebdavException { 1508 if (stNotificationDelay == ST_UNDEFINED) { 1509 return defaultValue; 1510 } 1511 else if (stNotificationDelay == ST_INVALID) { 1512 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1513 sendError( sc, "Invalid notification delay: "+hNotificationDelayStr ); 1514 throw new WebdavException( sc ); 1515 } 1516 else { 1517 return hNotificationDelay; 1518 } 1519 } 1520 1521 protected int getSubscriptionLifetime( int defaultValue ) throws WebdavException { 1522 if (stSubscriptionLifetime == ST_UNDEFINED) { 1523 return defaultValue; 1524 } 1525 else if (stSubscriptionLifetime == ST_INVALID) { 1526 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1527 sendError( sc, "Invalid subscription lifetime: "+hSubscriptionLifetimeStr ); 1528 throw new WebdavException( sc ); 1529 } 1530 else { 1531 return hSubscriptionLifetime; 1532 } 1533 } 1534 1535 protected String getTxId() throws WebdavException { 1536 if (stTxId == ST_UNDEFINED) { 1537 return null; 1538 } else if (stTxId == ST_INVALID) { 1539 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1540 sendError( sc, "Invalid transaction id: "+hTxIdStr ); 1541 throw new WebdavException( sc ); 1542 } 1543 else { 1544 return hTxId; 1545 } 1546 } 1547 1548 protected String getTxMethod() throws WebdavException { 1549 if (stTxMethod == ST_UNDEFINED) { 1550 return null; 1551 } else if (stTxMethod == ST_INVALID) { 1552 int sc = WebdavStatus.SC_PRECONDITION_FAILED; 1553 sendError( sc, "Invalid transaction method: "+hTxMethodStr ); 1554 throw new WebdavException( sc ); 1555 } 1556 else { 1557 return hTxMethod; 1558 } 1559 } 1560 1561 protected String getContentType() { 1562 if (stContentType == ST_UNDEFINED) { 1563 return null; 1564 } else { 1565 return hContentType; 1566 } 1567 } 1568 1569 protected void parse() { 1570 hTxIdStr = req.getHeader(H_TRANSACTION); 1572 if (hTxIdStr != null) { 1573 stTxId = ST_DEFINED; 1574 try { 1575 hTxId = hTxIdStr; 1576 } catch (Exception e) { 1577 stTxId = ST_INVALID; 1578 } 1579 } 1580 1581 hTxMethodStr = req.getHeader(H_TRANSACTION_METHOD); 1583 if (hTxMethodStr != null) { 1584 stTxMethod = ST_DEFINED; 1585 try { 1586 hTxMethod = hTxMethodStr; 1587 } catch (Exception e) { 1588 stTxMethod = ST_INVALID; 1589 } 1590 } 1591 1592 hNotificationTypeStr = req.getHeader(H_NOTIFICATION_TYPE); 1594 if (hNotificationTypeStr != null) { 1595 stNotificationType = ST_DEFINED; 1596 try { 1597 hNotificationType = hNotificationTypeStr; 1598 } catch (Exception e) { 1599 stNotificationType = ST_INVALID; 1600 } 1601 } 1602 1603 hNotificationDelayStr = req.getHeader(H_NOTIFICATION_DELAY); 1605 if (hNotificationDelayStr != null) { 1606 stNotificationDelay = ST_DEFINED; 1607 try { 1608 hNotificationDelay = Integer.parseInt(hNotificationDelayStr); 1609 } catch (Exception e) { 1610 stNotificationDelay = ST_INVALID; 1611 } 1612 } 1613 1614 hSubscriptionLifetimeStr = req.getHeader(H_SUBSCRIPTION_LIFETIME); 1616 if (hSubscriptionLifetimeStr != null) { 1617 stSubscriptionLifetime = ST_DEFINED; 1618 try { 1619 hSubscriptionLifetime = Integer.parseInt(hSubscriptionLifetimeStr); 1620 } catch (Exception e) { 1621 stSubscriptionLifetime = ST_INVALID; 1622 } 1623 } 1624 1625 hSubscriptionIdStr = req.getHeader(H_SUBSCRIPTION_ID); 1627 if (hSubscriptionIdStr != null) { 1628 stSubscriptionId = ST_DEFINED; 1629 try { 1630 StringTokenizer tokenizer = new StringTokenizer (hSubscriptionIdStr, ","); 1631 hSubscriptionId = new int[tokenizer.countTokens()]; 1632 int i = 0; 1633 while ( tokenizer.hasMoreTokens() ) { 1634 hSubscriptionId[i] = Integer.parseInt(tokenizer.nextToken().trim()); 1635 i++; 1636 } 1637 } catch (Exception e) { 1638 stSubscriptionId = ST_INVALID; 1639 } 1640 } 1641 1642 hCallbackStr = req.getHeader(H_CALL_BACK); 1644 if (hCallbackStr != null) { 1645 stCallback = ST_DEFINED; 1646 try { 1647 hCallback = hCallbackStr; 1648 } catch (Exception e) { 1649 stCallback = ST_INVALID; 1650 } 1651 } 1652 1653 hIfStr = req.getHeader(H_IF); 1655 if (hIfStr != null) { 1656 stIf = ST_DEFINED; 1657 try { 1658 hIf = extractLockTokens(hIfStr); 1659 } 1660 catch (Exception e) { 1661 stIf = ST_INVALID; 1662 } 1663 } 1664 1665 hLockTokenStr = req.getHeader(H_LOCK_TOKEN); 1667 if (hLockTokenStr != null) { 1668 stLockToken = ST_DEFINED; 1669 try { 1670 List tl = extractLockTokens(hLockTokenStr); 1671 hLockToken = (String )tl.get(0); 1672 } 1673 catch (Exception e) { 1674 stLockToken = ST_INVALID; 1675 } 1676 } 1677 1678 hDepthStr = req.getHeader(H_DEPTH); 1680 if (hDepthStr != null) { 1681 stDepth = ST_DEFINED; 1682 if ("0".equals(hDepthStr)) { 1683 hDepth = 0; 1684 } 1685 else if ("1".equals(hDepthStr)) { 1686 hDepth = 1; 1687 } 1688 else if ("infinity".equalsIgnoreCase(hDepthStr)) { 1689 hDepth = INFINITY; 1690 } 1691 else { 1692 stDepth = ST_INVALID; 1693 hDepth = Integer.parseInt(hDepthStr); 1694 } 1695 } 1696 1697 hDestinationStr = req.getHeader(H_DESTINATION); 1699 if (hDestinationStr != null) { 1700 stDestination = ST_DEFINED; 1701 hDestination = hDestinationStr; 1702 } 1703 1704 String hOverwriteStr = req.getHeader(H_OVERWRITE); 1706 if (hOverwriteStr != null) { 1707 stOverwrite = ST_DEFINED; 1708 if ("T".equalsIgnoreCase(hOverwriteStr)) { 1709 hOverwrite = true; 1710 } 1711 else if ("F".equalsIgnoreCase(hOverwriteStr)) { 1712 hOverwrite = false; 1713 } 1714 else { 1715 stOverwrite = ST_INVALID; 1716 } 1717 } 1718 1719 hTimeoutStr = req.getHeader(H_TIMEOUT); 1721 if (hTimeoutStr != null) { 1722 stTimeout = ST_DEFINED; 1723 try { 1724 hTimeout = extractLockDuration( hTimeoutStr ); 1725 } 1726 catch (Exception e) { 1727 stTimeout = ST_INVALID; 1728 } 1729 } 1730 1731 hLabelStr = req.getHeader(H_LABEL); 1733 if (hLabelStr != null) { 1734 stLabel = ST_DEFINED; 1735 hLabel = hLabelStr; 1736 } 1737 1738 hContentTypeStr = req.getHeader(H_CONTENT_TYPE); 1740 if (hContentTypeStr != null) { 1741 stContentType = ST_DEFINED; 1742 hContentType = hContentTypeStr; 1743 } 1744 1745 } 1746 1747 private List extractLockTokens(String hStr) { 1748 List result = new ArrayList (); 1749 int pos = hStr.indexOf(S_LOCK_TOKEN); 1750 int endPos = -1; 1751 int offset = S_LOCK_TOKEN.length(); 1752 String lockToken = null; 1753 1754 while (pos != -1) { 1755 1756 endPos = hStr.indexOf('>', pos + offset); 1757 if (endPos == -1) { 1758 lockToken = hStr; 1759 endPos = hStr.length(); 1760 } else { 1761 lockToken = hStr.substring(pos + offset, endPos); 1762 } 1763 1764 slideToken.addLockToken(lockToken); 1766 result.add( lockToken ); 1767 pos = hStr.indexOf(S_LOCK_TOKEN, endPos); 1768 } 1769 return result; 1770 } 1771 1772 private int extractLockDuration(String hStr) { 1773 int result; 1774 int firstCommaPos = hStr.indexOf(','); 1775 if (firstCommaPos != -1) { 1776 hStr = hStr.substring(0, firstCommaPos); 1777 } 1778 if (hStr.startsWith("Second-")) { 1779 result = Integer.parseInt(hStr.substring("Second-".length())); 1780 } else { 1781 if (hStr.equalsIgnoreCase("Infinite")) { 1782 result = INFINITY; 1783 } else { 1784 result = Integer.parseInt(hStr); 1785 } 1786 } 1787 return result; 1788 } 1789 } 1790} | Popular Tags |