1 23 24 package org.apache.slide.webdav.method; 25 26 import java.io.IOException ; 27 import java.io.Writer ; 28 import java.util.Date ; 29 import java.util.Iterator ; 30 import java.util.List ; 31 import java.util.StringTokenizer ; 32 33 import org.apache.slide.common.NamespaceAccessToken; 34 import org.apache.slide.common.PropertyParseException; 35 import org.apache.slide.common.RequestedPropertiesImpl; 36 import org.apache.slide.common.RequestedProperty; 37 import org.apache.slide.common.RequestedPropertyImpl; 38 import org.apache.slide.common.ServiceAccessException; 39 import org.apache.slide.content.NodeProperty; 40 import org.apache.slide.content.NodeRevisionDescriptor; 41 import org.apache.slide.content.NodeRevisionDescriptors; 42 import org.apache.slide.content.NodeRevisionNumber; 43 import org.apache.slide.content.RevisionDescriptorNotFoundException; 44 import org.apache.slide.content.NodeProperty.NamespaceCache; 45 import org.apache.slide.event.EventDispatcher; 46 import org.apache.slide.structure.LinkedObjectNotFoundException; 47 import org.apache.slide.util.Configuration; 48 import org.apache.slide.util.XMLValue; 49 import org.apache.slide.webdav.WebdavException; 50 import org.apache.slide.webdav.WebdavServletConfig; 51 import org.apache.slide.webdav.event.WebdavEvent; 52 import org.apache.slide.webdav.util.AclConstants; 53 import org.apache.slide.webdav.util.DeltavConstants; 54 import org.apache.slide.webdav.util.PreconditionViolationException; 55 import org.apache.slide.webdav.util.PropertyHelper; 56 import org.apache.slide.webdav.util.UriHandler; 57 import org.apache.slide.webdav.util.VersioningHelper; 58 import org.apache.slide.webdav.util.ViolatedPrecondition; 59 import org.apache.slide.webdav.util.WebdavStatus; 60 import org.apache.slide.webdav.util.resourcekind.AbstractResourceKind; 61 import org.apache.slide.webdav.util.resourcekind.CheckedInVersionControlled; 62 import org.apache.slide.webdav.util.resourcekind.ResourceKind; 63 import org.jdom.CDATA; 64 import org.jdom.Comment; 65 import org.jdom.Document; 66 import org.jdom.Element; 67 import org.jdom.EntityRef; 68 import org.jdom.JDOMException; 69 import org.jdom.Namespace; 70 import org.jdom.ProcessingInstruction; 71 import org.jdom.Text; 72 import org.jdom.output.Format; 73 import org.jdom.output.XMLOutputter; 74 75 79 public class PropPatchMethod extends AbstractWebdavMethod implements DeltavConstants, AclConstants, WriteMethod { 80 81 82 84 85 87 88 91 protected VersioningHelper versioningHelper = null; 92 protected PropertyHelper propertyHelper = null; 93 94 97 private PropPatchProperties propertiesToSet; 98 99 102 private PropPatchProperties propertiesToRemove; 103 104 107 private String resourcePath; 108 109 112 private boolean updateLastModified; 113 114 116 117 123 public PropPatchMethod(NamespaceAccessToken token, 124 WebdavServletConfig config) { 125 super(token, config); 126 } 127 128 129 131 132 137 protected void parseRequest() 138 throws WebdavException { 139 140 versioningHelper = 141 VersioningHelper.getVersioningHelper( slideToken, token, req, resp, config ); 142 propertyHelper = 143 PropertyHelper.getPropertyHelper( slideToken, token, getConfig() ); 144 145 147 updateLastModified = getBooleanInitParameter( "updateLastModified" ); 148 149 resourcePath = requestUri; 150 if (resourcePath == null) { 151 resourcePath = "/"; 152 } 153 propertiesToSet = new PropPatchProperties(); 154 propertiesToRemove = new PropPatchProperties(); 155 156 if (req.getContentLength() != 0) { 157 158 try { 159 Iterator childrenIterator = parseRequestContent(E_PROPERTYUPDATE).getChildren().iterator(); 160 Element child = null; 161 while (childrenIterator.hasNext()) { 162 child = (Element)childrenIterator.next(); 163 if (E_SET.equals(child.getName())) { 164 propertiesToSet.add(getPropElement(child)); 165 } 166 else if (E_REMOVE.equals(child.getName())) { 167 propertiesToRemove.add(getPropElement(child)); 168 } 169 else { 170 throw new JDOMException("Expected <"+E_SET+"> or <"+E_REMOVE+"> element"); 171 } 172 } 173 174 175 } 176 catch( Exception e ) { 177 int statusCode = getErrorCode( e ); 178 sendError( statusCode, e ); 179 throw new WebdavException( statusCode ); 180 } 181 } 182 else { 183 int statusCode = WebdavStatus.SC_BAD_REQUEST; 184 sendError( statusCode, getClass().getName()+".missingRequestBody" ); 185 throw new WebdavException( statusCode ); 186 } 187 188 } 189 190 202 private Element getPropElement(Element parent) throws JDOMException { 203 List childrenList = parent.getChildren(); 204 if ( (childrenList.size() != 1) || 205 ( ! E_PROP.equals(((Element)childrenList.get(0)).getName()) ) ) { 206 throw new JDOMException("Expected <"+E_PROP+"> element"); 207 } 208 return (Element)childrenList.get(0); 209 } 210 211 212 217 protected void executeRequest() 218 throws WebdavException, IOException { 219 220 boolean allOperationsExcecuted = true; 221 222 slideToken.setForceStoreEnlistment(true); 224 225 try { 227 if (isLockNull(resourcePath)) { 228 int statusCode = WebdavStatus.SC_NOT_FOUND; 229 sendError( statusCode, "lock-null resource", new Object []{resourcePath} ); 230 throw new WebdavException( statusCode ); 231 } 232 } 233 catch (ServiceAccessException e) { 234 int statusCode = getErrorCode((Exception )e); 235 sendError( statusCode, e ); 236 throw new WebdavException( statusCode ); 237 } 238 239 try { 240 if ( WebdavEvent.PROPPATCH.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(WebdavEvent.PROPPATCH, new WebdavEvent(this)); 241 242 VersioningHelper vHelp = 243 VersioningHelper.getVersioningHelper(slideToken, token, req, resp, getConfig() ); 244 NodeRevisionDescriptors revisionDescriptors = 245 content.retrieve(slideToken, resourcePath); 246 247 NodeRevisionNumber revisionNumber = 248 revisionDescriptors.getLatestRevision(); 249 NodeRevisionDescriptor revisionDescriptor = null; 250 if (revisionNumber != null) { 251 try { 252 revisionDescriptor = content.retrieve 253 (slideToken, revisionDescriptors); 254 } catch (RevisionDescriptorNotFoundException e) { 255 } 256 } 257 258 if (revisionDescriptor == null) { 259 revisionDescriptor = new NodeRevisionDescriptor(0); 260 } 261 262 ResourceKind resourceKind = AbstractResourceKind.determineResourceKind(token, resourcePath, revisionDescriptor); 263 264 ViolatedPrecondition violatedPrecondition = getPreconditionViolation(revisionDescriptors, revisionDescriptor, resourceKind); 266 if (violatedPrecondition != null) { 267 throw new PreconditionViolationException(violatedPrecondition, resourcePath); 268 } 269 270 boolean mustCheckIn = false; 272 if( Configuration.useVersionControl() && 273 (resourceKind instanceof CheckedInVersionControlled) && 274 versioningHelper.mustCheckoutAutoVersionedVCR(revisionDescriptors, revisionDescriptor) ) { 275 276 vHelp.checkout(revisionDescriptors, revisionDescriptor, false, false, true ); 277 mustCheckIn = versioningHelper.mustCheckinAutoVersionedVCR(slideToken, revisionDescriptors, revisionDescriptor); 278 } 279 280 282 Iterator propertyIterator = null; 283 284 propertyIterator = propertiesToSet.iterator(); 285 PropPatchProperty currentProperty = null; 286 while (propertyIterator.hasNext()) { 287 288 currentProperty = (PropPatchProperty)propertyIterator.next(); 289 if (checkPropertyModification(currentProperty, revisionDescriptor, resourceKind)) { 290 String finalValue = currentProperty.getValue(); 293 Element property = new Element(currentProperty.getName(), currentProperty.getNamespace()); 294 String propertyValue = currentProperty.getValue(); 295 if ( (propertyValue != null) && (propertyValue.toString().length() > 0)) { 296 if( propertyValue.toString().indexOf('<') >= 0 ) { 297 try { 298 XMLValue xmlValue = new XMLValue(propertyValue.toString(), Namespace.getNamespace(currentProperty.getNamespace())); 299 if (AbstractResourceKind.isLiveProperty(currentProperty.getName())) { 300 convertHrefValueToRelativeURL (xmlValue, getSlideContextPath(), config); 301 } 302 Iterator iterator = xmlValue.iterator(); 303 while (iterator.hasNext()) { 304 Object o = iterator.next(); 305 if( o instanceof Element ) 306 property.addContent((Element)o); 307 else if( o instanceof Text ) 308 property.addContent((Text)o); 309 else if( o instanceof Comment ) 310 property.addContent((Comment)o); 311 else if( o instanceof ProcessingInstruction ) 312 property.addContent((ProcessingInstruction)o); 313 else if( o instanceof CDATA ) 314 property.addContent((CDATA)o); 315 else if( o instanceof EntityRef ) 316 property.addContent((EntityRef)o); 317 } 318 finalValue = new XMLOutputter(Format.getRawFormat()).outputString(property.getContent()); 319 } 320 catch (JDOMException e) { 321 } 323 } 324 } 325 NodeProperty newProperty = 326 new NodeProperty(currentProperty.getName(), 327 finalValue, 328 currentProperty.getNamespace()); 329 revisionDescriptor.setProperty(newProperty); 330 331 } 332 else { 333 allOperationsExcecuted = false; 334 } 335 } 336 337 propertyIterator = propertiesToRemove.iterator(); 338 while (propertyIterator.hasNext()) { 339 340 currentProperty = (PropPatchProperty)propertyIterator.next(); 341 if (checkPropertyModification(currentProperty, revisionDescriptor, resourceKind)) { 342 revisionDescriptor.removeProperty(currentProperty.getName(), 343 currentProperty.getNamespace()); 344 } 345 else { 346 allOperationsExcecuted = false; 347 } 348 349 } 350 351 if (updateLastModified) { 352 revisionDescriptor.setLastModified(new Date ()); 353 } 354 355 if (allOperationsExcecuted) { 356 content.store(slideToken, resourcePath, revisionDescriptor, null); 357 } 358 359 if( Configuration.useVersionControl() && mustCheckIn) { 361 vHelp.checkin(revisionDescriptors, revisionDescriptor, false, false, true ); } 363 365 resp.setContentType(TEXT_XML_UTF_8); 366 367 resp.setStatus(WebdavStatus.SC_MULTI_STATUS); 368 369 } 370 catch (PreconditionViolationException e) { 371 sendPreconditionViolation(e); 372 throw e; 373 } 374 catch (Exception e) { 375 int statusCode = getErrorCode( e ); 376 sendError( statusCode, e ); 377 throw new WebdavException( statusCode ); 378 } 379 380 381 writeReport(); 383 384 if (!allOperationsExcecuted) { 385 throw new WebdavException( WebdavStatus.SC_ACCEPTED ); } 387 388 } 389 390 391 392 393 396 protected int getErrorCode(Exception ex) { 397 try { 398 throw ex; 399 } catch (LinkedObjectNotFoundException e) { 400 return WebdavStatus.SC_NOT_FOUND; 401 } catch (PropertyParseException e) { 402 return WebdavStatus.SC_BAD_REQUEST; 403 } catch (JDOMException e) { 404 return WebdavStatus.SC_BAD_REQUEST; 405 } catch (Exception e) { 406 return super.getErrorCode(e); 407 } 408 } 409 410 411 412 414 415 416 417 418 426 private boolean checkPropertyModification(PropPatchProperty property, NodeRevisionDescriptor rd, ResourceKind resourceKind) { 427 428 boolean result = false; 429 ViolatedPrecondition violatedPrecondition = getPropertySpecificPreconditionViolation(property); 430 if (violatedPrecondition != null) { 431 property.setViolatedPrecondition(violatedPrecondition); 432 } 433 else { 434 NodeProperty originalProperty = rd.getProperty(property.getName(), property.getNamespace()); 435 if (originalProperty == null) { 436 result = (!AbstractResourceKind.isComputedProperty(property.getName())); 438 } 439 else { 440 result = !originalProperty.isProtected(); 441 } 442 443 if ( ! result ) { 444 property.setErrorMessage("Property " + property.getQualifiedNameAsElementString() + " is protected"); 445 } 446 447 if ( result && !isSupportedPropertyValue(property, resourceKind) ) { 448 property.setErrorMessage("Value " + property.getValue() + " is not supported by property " + property.getQualifiedNameAsElementString()); 449 result = false; 450 } 451 452 if (!result) property.setStatusCode(WebdavStatus.SC_CONFLICT); 453 } 454 return result; 455 } 456 457 466 private boolean isSupportedPropertyValue(PropPatchProperty property, ResourceKind resourceKind) { 467 468 boolean isSupportedValue = true; 469 if (property.getValue() != null) { 470 471 if ( ! resourceKind.isSupportedPropertyValue(property.getName(), property.getValue()) ) { 472 isSupportedValue = false; 473 } 474 475 if (P_GETCONTENTLANGUAGE.equals(property.getName())) { 476 477 StringTokenizer tokenizer = new StringTokenizer (property.getValue(), "-"); 478 String token = null; 479 while ( isSupportedValue && tokenizer.hasMoreTokens() ) { 480 token = tokenizer.nextToken(); 481 isSupportedValue = ( (token.length() >= 1) && (token.length() <= 8) ); 482 int i = 0; 483 while (isSupportedValue && (i < token.length()) ) { 484 char character = token.charAt(i); 485 isSupportedValue = 486 ((character >= 'a') && (character <= 'z')) || 487 ((character >= 'A') && (character <= 'Z')); 488 ++i; 489 } 490 } 491 } 492 493 } 494 return isSupportedValue; 495 } 496 497 498 499 502 private void writeReport() 503 throws WebdavException { 504 505 Element multistatus = new Element(E_MULTISTATUS, DNSP); 506 Element response = new Element(E_RESPONSE, DNSP); 507 multistatus.addContent(response); 508 Element href = new Element(E_HREF, DNSP); 509 href.setText(getFullPath(requestUri)); 510 response.addContent(href); 511 512 addPropstatElements(propertiesToSet, response); 514 addPropstatElements(propertiesToRemove, response); 515 516 try { 517 resp.setContentType(TEXT_XML_UTF_8); 518 Writer writer = resp.getWriter(); 519 org.jdom.output.Format format = org.jdom.output.Format.getPrettyFormat(); 520 format.setIndent(XML_RESPONSE_INDENT); 521 new XMLOutputter(format). 522 output(new Document(multistatus), writer); 523 writer.flush(); 524 } catch (Exception e) { 525 int statusCode = WebdavStatus.SC_INTERNAL_SERVER_ERROR; 526 sendError( statusCode, e ); 527 throw new WebdavException( statusCode ); 528 } 529 530 } 531 532 542 private void addPropstatElements(PropPatchProperties properties, Element response) { 543 544 Iterator propertyIterator = properties.iterator(); 545 while(propertyIterator.hasNext()) { 546 PropPatchProperty property = (PropPatchProperty) propertyIterator.next(); 547 Element propstat = createPropstatElement(property); 548 response.addContent(propstat); 549 if (property.getViolatedPrecondition() != null) { 550 Element responseDescription = new Element(E_RESPONSEDESCRIPTION, DNSP); 551 responseDescription.addContent(MethodUtil.getPreconditionViolationError(property.getViolatedPrecondition())); 552 propstat.addContent(responseDescription); 553 } 554 else if (property.getErrorMessage() != null) { 555 Element responseDescription = new Element(E_RESPONSEDESCRIPTION, DNSP); 556 responseDescription.addContent(property.getErrorMessage()); 557 propstat.addContent(responseDescription); 558 } 559 } 560 } 561 562 571 private Element createPropstatElement(PropPatchProperty property) { 572 573 Element propstat = new Element(E_PROPSTAT, DNSP); 574 Element prop = new Element(E_PROP, DNSP); 575 propstat.addContent(prop); 576 577 Namespace namespace = Namespace.NO_NAMESPACE; 578 if (property.getNamespace() != null) { 579 namespace = NamespaceCache.getNamespace(property.getNamespace()); 580 } 581 Element propertyElement = new Element(property.getName(), namespace); 582 prop.addContent(propertyElement); 583 584 Element status = new Element(E_STATUS, DNSP); 585 status.setText("HTTP/1.1 " + property.statusCode + " " 586 + WebdavStatus.getStatusText(property.statusCode)); 587 propstat.addContent(status); 588 return propstat; 589 } 590 591 608 private ViolatedPrecondition getPreconditionViolation(NodeRevisionDescriptors revisionDescriptors, NodeRevisionDescriptor revisionDescriptor, ResourceKind resourceKind) 609 throws ServiceAccessException { 610 611 if( Configuration.useVersionControl() ) { 612 613 if (resourceKind instanceof CheckedInVersionControlled) { 614 615 String autoVersion = versioningHelper.getAutoVersionElementName(revisionDescriptor); 617 if (autoVersion == null) { 618 autoVersion = ""; 619 } 620 621 if ( !E_CHECKOUT_CHECKIN.equals(autoVersion) && 622 !E_CHECKOUT_UNLOCKED_CHECKIN.equals(autoVersion) && 623 !E_CHECKOUT.equals(autoVersion) && 624 !E_CHECKOUT_IGNORE_UNLOCK.equals(autoVersion) && 625 !E_LOCKED_CHECKOUT.equals(autoVersion) ) { 626 return new ViolatedPrecondition(C_CANNOT_MODIFY_VERSION_CONTROLLED_PROPERTY, 627 WebdavStatus.SC_FORBIDDEN); 628 } 629 if (E_LOCKED_CHECKOUT.equals(autoVersion)) { 630 if ( !versioningHelper.isWriteLocked(slideToken, revisionDescriptors) ) { 631 return new ViolatedPrecondition(C_CANNOT_MODIFY_VERSION_CONTROLLED_PROPERTY, 632 WebdavStatus.SC_FORBIDDEN); 633 } 634 } 635 } 636 637 UriHandler uriHandler = UriHandler.getUriHandler(resourcePath); 639 if (uriHandler.isVersionUri()) { 640 return new ViolatedPrecondition(C_CANNOT_MODIFY_VERSION, 641 WebdavStatus.SC_FORBIDDEN); 642 } 643 } 644 645 return null; 646 } 647 648 660 private ViolatedPrecondition getPropertySpecificPreconditionViolation(PropPatchProperty property) { 661 662 if ( AbstractResourceKind.isProtectedProperty(property.getName()) && 664 DeltavConstants.DELTAV_PROPERTY_LIST.contains(property.getName()) ) { 665 return new ViolatedPrecondition(C_CANNOT_MODIFY_PROTECTED_PROPERTY, 666 WebdavStatus.SC_CONFLICT); 667 } 668 669 672 return null; 673 } 674 675 676 677 689 private static void convertHrefValueToRelativeURL (XMLValue xmlValue, 690 String servletContextPath, 691 WebdavServletConfig config) { 692 if (xmlValue != null) { 693 Iterator iterator = xmlValue.iterator(); 694 Element element = null; 695 while (iterator.hasNext()) { 696 Object o = iterator.next(); 697 if( o instanceof Element ) { 698 element = (Element)o; 699 convertHrefValueToRelativeURL(element, servletContextPath, config); 700 } 701 } 702 } 703 } 704 705 706 707 720 private static void convertHrefValueToRelativeURL (Element element, 721 String servletContextPath, 722 WebdavServletConfig config) { 723 if (element.getChildren().size() > 0) { 724 Iterator i = element.getChildren().iterator(); 725 while (i.hasNext()) { 726 Element child = (Element) i.next(); 727 convertHrefValueToRelativeURL(child, servletContextPath, config); 728 } 729 } 730 if ( E_HREF.equals(element.getName()) && (element.getText() != null) ) { 731 if ( PropertyHelper.isAbsoluteURL(servletContextPath, element.getText()) ) { 732 element.setText(element.getText().substring(servletContextPath.length())); 733 } 734 } 735 } 736 737 738 739 743 public class PropPatchProperties extends RequestedPropertiesImpl { 744 745 748 public PropPatchProperties() { 749 super(); 750 } 751 752 760 public PropPatchProperties (Element propElement) throws PropertyParseException { 761 super(propElement); 762 } 763 764 777 protected RequestedProperty createRequestedProperty(String name, String namespacePrefix, String namespaceUri, List content) { 778 779 String value = ""; 780 if (content.size() == 1 && content.get(0) instanceof Text) { 781 value = ((Text)content.get(0)).getText(); 782 } 783 else if (content.size() > 0) { 784 XMLValue xmlVal = new XMLValue(content); 785 if (P_GROUP_MEMBER_SET.equals(name) && S_DAV.equals(namespaceUri)) { 786 xmlVal.stripServletContext(getSlideContextPath()); 787 } 788 value = xmlVal.toString(); 789 } 790 NamespaceCache.getNamespace(namespacePrefix, namespaceUri); 792 793 return new PropPatchProperty(name, namespaceUri, value); 794 } 795 796 } 797 798 801 public static class PropPatchProperty extends RequestedPropertyImpl { 802 803 806 protected String value = null; 807 808 811 protected int statusCode = WebdavStatus.SC_OK; 812 813 817 protected ViolatedPrecondition violatedPrecondition = null; 818 819 823 protected String errorMessage = null; 824 825 831 protected String qualifiedNameAsElement = null; 832 833 834 840 public PropPatchProperty (String propertyName) { 841 super(propertyName); 842 } 843 844 850 public PropPatchProperty (String propertyName, String namespace) { 851 super(propertyName, namespace); 852 } 853 854 861 public PropPatchProperty (String propertyName, String namespace, String value) { 862 super(propertyName, namespace); 863 this.value = value; 864 } 865 866 871 public String getValue() { 872 return value; 873 } 874 875 881 public void setStatusCode(int statusCode) { 882 this.statusCode = statusCode; 883 } 884 885 890 public int getStatusCode() { 891 return statusCode; 892 } 893 894 901 public void setErrorMessage(String errorMessage) { 902 this.errorMessage = errorMessage; 903 } 904 905 911 public String getErrorMessage() { 912 return errorMessage; 913 } 914 915 923 public void setViolatedPrecondition(ViolatedPrecondition violatedPrecondition) { 924 this.violatedPrecondition = violatedPrecondition; 925 if (violatedPrecondition != null) { 926 setStatusCode(violatedPrecondition.getStatusCode()); 927 } 928 } 929 930 937 public ViolatedPrecondition getViolatedPrecondition() { 938 return violatedPrecondition; 939 } 940 941 949 public String getQualifiedNameAsElementString() { 950 if (qualifiedNameAsElement == null) { 951 StringBuffer buffer = new StringBuffer (); 952 buffer.append("<"); 953 buffer.append(getName()); 954 if ( (getNamespace() != null) && (getNamespace().length() > 0) ) { 955 buffer.append(" xmlns=\""); 956 buffer.append(getNamespace()); 957 buffer.append("\""); 958 buffer.append(" />"); 959 } 960 qualifiedNameAsElement = buffer.toString(); 961 } 962 return qualifiedNameAsElement; 963 } 964 965 970 public String toString() { 971 return getNamespace()+":"+getName()+"["+getValue()+"]"; 972 } 973 } 974 975 } 976 977 | Popular Tags |