| 1 17 18 19 package org.apache.catalina.servlets; 20 21 22 import java.io.IOException ; 23 import java.io.StringWriter ; 24 import java.io.Writer ; 25 import java.security.MessageDigest ; 26 import java.security.NoSuchAlgorithmException ; 27 import java.text.SimpleDateFormat ; 28 import java.util.Date ; 29 import java.util.Enumeration ; 30 import java.util.Hashtable ; 31 import java.util.Stack ; 32 import java.util.TimeZone ; 33 import java.util.Vector ; 34 35 import javax.naming.NameClassPair ; 36 import javax.naming.NamingEnumeration ; 37 import javax.naming.NamingException ; 38 import javax.naming.directory.DirContext ; 39 import javax.servlet.ServletException ; 40 import javax.servlet.UnavailableException ; 41 import javax.servlet.http.HttpServletRequest ; 42 import javax.servlet.http.HttpServletResponse ; 43 import javax.xml.parsers.DocumentBuilder ; 44 import javax.xml.parsers.DocumentBuilderFactory ; 45 import javax.xml.parsers.ParserConfigurationException ; 46 47 import org.apache.catalina.util.DOMWriter; 48 import org.apache.catalina.util.MD5Encoder; 49 import org.apache.catalina.util.RequestUtil; 50 import org.apache.catalina.util.XMLWriter; 51 import org.apache.naming.resources.CacheEntry; 52 import org.apache.naming.resources.Resource; 53 import org.apache.naming.resources.ResourceAttributes; 54 import org.apache.tomcat.util.http.FastHttpDateFormat; 55 import org.w3c.dom.Document ; 56 import org.w3c.dom.Element ; 57 import org.w3c.dom.Node ; 58 import org.w3c.dom.NodeList ; 59 import org.xml.sax.InputSource ; 60 import org.xml.sax.SAXException ; 61 62 63 64 71 72 public class WebdavServlet 73 extends DefaultServlet { 74 75 76 78 79 private static final String METHOD_HEAD = "HEAD"; 80 private static final String METHOD_PROPFIND = "PROPFIND"; 81 private static final String METHOD_PROPPATCH = "PROPPATCH"; 82 private static final String METHOD_MKCOL = "MKCOL"; 83 private static final String METHOD_COPY = "COPY"; 84 private static final String METHOD_MOVE = "MOVE"; 85 private static final String METHOD_LOCK = "LOCK"; 86 private static final String METHOD_UNLOCK = "UNLOCK"; 87 88 89 92 private static final int INFINITY = 3; 94 95 98 private static final int FIND_BY_PROPERTY = 0; 99 100 101 104 private static final int FIND_ALL_PROP = 1; 105 106 107 110 private static final int FIND_PROPERTY_NAMES = 2; 111 112 113 116 private static final int LOCK_CREATION = 0; 117 118 119 122 private static final int LOCK_REFRESH = 1; 123 124 125 128 private static final int DEFAULT_TIMEOUT = 3600; 129 130 131 134 private static final int MAX_TIMEOUT = 604800; 135 136 137 140 protected static final String DEFAULT_NAMESPACE = "DAV:"; 141 142 143 146 protected static final SimpleDateFormat creationDateFormat = 147 new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ss'Z'"); 148 149 150 153 protected static MessageDigest md5Helper; 154 155 156 159 protected static final MD5Encoder md5Encoder = new MD5Encoder(); 160 161 162 163 static { 164 creationDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); 165 } 166 167 168 170 171 177 private Hashtable resourceLocks = new Hashtable (); 178 179 180 188 private Hashtable lockNullResources = new Hashtable (); 189 190 191 197 private Vector collectionLocks = new Vector (); 198 199 200 203 private String secret = "catalina"; 204 205 206 208 209 212 public void init() 213 throws ServletException { 214 215 super.init(); 216 217 if (getServletConfig().getInitParameter("secret") != null) 218 secret = getServletConfig().getInitParameter("secret"); 219 220 try { 222 md5Helper = MessageDigest.getInstance("MD5"); 223 } catch (NoSuchAlgorithmException e) { 224 throw new UnavailableException ("No MD5"); 225 } 226 227 } 228 229 230 232 233 236 protected DocumentBuilder getDocumentBuilder() 237 throws ServletException { 238 DocumentBuilder documentBuilder = null; 239 DocumentBuilderFactory documentBuilderFactory = null; 240 try { 241 documentBuilderFactory = DocumentBuilderFactory.newInstance(); 242 documentBuilderFactory.setNamespaceAware(true); 243 documentBuilder = documentBuilderFactory.newDocumentBuilder(); 244 } catch(ParserConfigurationException e) { 245 throw new ServletException  246 (sm.getString("webdavservlet.jaxpfailed")); 247 } 248 return documentBuilder; 249 } 250 251 252 255 protected void service(HttpServletRequest req, HttpServletResponse resp) 256 throws ServletException , IOException { 257 258 String method = req.getMethod(); 259 260 if (debug > 0) { 261 String path = getRelativePath(req); 262 log("[" + method + "] " + path); 263 } 264 265 if (method.equals(METHOD_PROPFIND)) { 266 doPropfind(req, resp); 267 } else if (method.equals(METHOD_PROPPATCH)) { 268 doProppatch(req, resp); 269 } else if (method.equals(METHOD_MKCOL)) { 270 doMkcol(req, resp); 271 } else if (method.equals(METHOD_COPY)) { 272 doCopy(req, resp); 273 } else if (method.equals(METHOD_MOVE)) { 274 doMove(req, resp); 275 } else if (method.equals(METHOD_LOCK)) { 276 doLock(req, resp); 277 } else if (method.equals(METHOD_UNLOCK)) { 278 doUnlock(req, resp); 279 } else { 280 super.service(req, resp); 282 } 283 284 } 285 286 287 298 protected boolean checkIfHeaders(HttpServletRequest request, 299 HttpServletResponse response, 300 ResourceAttributes resourceAttributes) 301 throws IOException { 302 303 if (!super.checkIfHeaders(request, response, resourceAttributes)) 304 return false; 305 306 return true; 308 309 } 310 311 312 320 protected void doOptions(HttpServletRequest req, HttpServletResponse resp) 321 throws ServletException , IOException { 322 323 resp.addHeader("DAV", "1,2"); 324 325 StringBuffer methodsAllowed = determineMethodsAllowed(resources, 326 req); 327 328 resp.addHeader("Allow", methodsAllowed.toString()); 329 resp.addHeader("MS-Author-Via", "DAV"); 330 331 } 332 333 334 337 protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) 338 throws ServletException , IOException { 339 340 if (!listings) { 341 StringBuffer methodsAllowed = determineMethodsAllowed(resources, 343 req); 344 345 resp.addHeader("Allow", methodsAllowed.toString()); 346 resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED); 347 return; 348 } 349 350 String path = getRelativePath(req); 351 if (path.endsWith("/")) 352 path = path.substring(0, path.length() - 1); 353 354 if ((path.toUpperCase().startsWith("/WEB-INF")) || 355 (path.toUpperCase().startsWith("/META-INF"))) { 356 resp.sendError(WebdavStatus.SC_FORBIDDEN); 357 return; 358 } 359 360 Vector properties = null; 362 int depth = INFINITY; 364 int type = FIND_ALL_PROP; 366 367 String depthStr = req.getHeader("Depth"); 368 369 if (depthStr == null) { 370 depth = INFINITY; 371 } else { 372 if (depthStr.equals("0")) { 373 depth = 0; 374 } else if (depthStr.equals("1")) { 375 depth = 1; 376 } else if (depthStr.equals("infinity")) { 377 depth = INFINITY; 378 } 379 } 380 381 Node propNode = null; 382 383 DocumentBuilder documentBuilder = getDocumentBuilder(); 384 385 try { 386 Document document = documentBuilder.parse 387 (new InputSource (req.getInputStream())); 388 389 Element rootElement = document.getDocumentElement(); 391 NodeList childList = rootElement.getChildNodes(); 392 393 for (int i=0; i < childList.getLength(); i++) { 394 Node currentNode = childList.item(i); 395 switch (currentNode.getNodeType()) { 396 case Node.TEXT_NODE: 397 break; 398 case Node.ELEMENT_NODE: 399 if (currentNode.getNodeName().endsWith("prop")) { 400 type = FIND_BY_PROPERTY; 401 propNode = currentNode; 402 } 403 if (currentNode.getNodeName().endsWith("propname")) { 404 type = FIND_PROPERTY_NAMES; 405 } 406 if (currentNode.getNodeName().endsWith("allprop")) { 407 type = FIND_ALL_PROP; 408 } 409 break; 410 } 411 } 412 } catch (SAXException e) { 413 } catch (IOException e) { 415 } 417 418 if (type == FIND_BY_PROPERTY) { 419 properties = new Vector (); 420 NodeList childList = propNode.getChildNodes(); 421 422 for (int i=0; i < childList.getLength(); i++) { 423 Node currentNode = childList.item(i); 424 switch (currentNode.getNodeType()) { 425 case Node.TEXT_NODE: 426 break; 427 case Node.ELEMENT_NODE: 428 String nodeName = currentNode.getNodeName(); 429 String propertyName = null; 430 if (nodeName.indexOf(':') != -1) { 431 propertyName = nodeName.substring 432 (nodeName.indexOf(':') + 1); 433 } else { 434 propertyName = nodeName; 435 } 436 properties.addElement(propertyName); 438 break; 439 } 440 } 441 442 } 443 444 boolean exists = true; 445 Object object = null; 446 try { 447 object = resources.lookup(path); 448 } catch (NamingException e) { 449 exists = false; 450 int slash = path.lastIndexOf('/'); 451 if (slash != -1) { 452 String parentPath = path.substring(0, slash); 453 Vector currentLockNullResources = 454 (Vector ) lockNullResources.get(parentPath); 455 if (currentLockNullResources != null) { 456 Enumeration lockNullResourcesList = 457 currentLockNullResources.elements(); 458 while (lockNullResourcesList.hasMoreElements()) { 459 String lockNullPath = (String ) 460 lockNullResourcesList.nextElement(); 461 if (lockNullPath.equals(path)) { 462 resp.setStatus(WebdavStatus.SC_MULTI_STATUS); 463 resp.setContentType("text/xml; charset=UTF-8"); 464 XMLWriter generatedXML = 466 new XMLWriter(resp.getWriter()); 467 generatedXML.writeXMLHeader(); 468 generatedXML.writeElement 469 (null, "multistatus" 470 + generateNamespaceDeclarations(), 471 XMLWriter.OPENING); 472 parseLockNullProperties 473 (req, generatedXML, lockNullPath, type, 474 properties); 475 generatedXML.writeElement(null, "multistatus", 476 XMLWriter.CLOSING); 477 generatedXML.sendData(); 478 return; 479 } 480 } 481 } 482 } 483 } 484 485 if (!exists) { 486 resp.sendError(HttpServletResponse.SC_NOT_FOUND, path); 487 return; 488 } 489 490 resp.setStatus(WebdavStatus.SC_MULTI_STATUS); 491 492 resp.setContentType("text/xml; charset=UTF-8"); 493 494 XMLWriter generatedXML = new XMLWriter(resp.getWriter()); 496 generatedXML.writeXMLHeader(); 497 498 generatedXML.writeElement(null, "multistatus" 499 + generateNamespaceDeclarations(), 500 XMLWriter.OPENING); 501 502 if (depth == 0) { 503 parseProperties(req, generatedXML, path, type, 504 properties); 505 } else { 506 Stack stack = new Stack (); 508 stack.push(path); 509 510 Stack stackBelow = new Stack (); 512 513 while ((!stack.isEmpty()) && (depth >= 0)) { 514 515 String currentPath = (String ) stack.pop(); 516 parseProperties(req, generatedXML, currentPath, 517 type, properties); 518 519 try { 520 object = resources.lookup(currentPath); 521 } catch (NamingException e) { 522 continue; 523 } 524 525 if ((object instanceof DirContext ) && (depth > 0)) { 526 527 try { 528 NamingEnumeration enumeration = resources.list(currentPath); 529 while (enumeration.hasMoreElements()) { 530 NameClassPair ncPair = 531 (NameClassPair ) enumeration.nextElement(); 532 String newPath = currentPath; 533 if (!(newPath.endsWith("/"))) 534 newPath += "/"; 535 newPath += ncPair.getName(); 536 stackBelow.push(newPath); 537 } 538 } catch (NamingException e) { 539 resp.sendError 540 (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 541 path); 542 return; 543 } 544 545 String lockPath = currentPath; 548 if (lockPath.endsWith("/")) 549 lockPath = 550 lockPath.substring(0, lockPath.length() - 1); 551 Vector currentLockNullResources = 552 (Vector ) lockNullResources.get(lockPath); 553 if (currentLockNullResources != null) { 554 Enumeration lockNullResourcesList = 555 currentLockNullResources.elements(); 556 while (lockNullResourcesList.hasMoreElements()) { 557 String lockNullPath = (String ) 558 lockNullResourcesList.nextElement(); 559 parseLockNullProperties 560 (req, generatedXML, lockNullPath, type, 561 properties); 562 } 563 } 564 565 } 566 567 if (stack.isEmpty()) { 568 depth--; 569 stack = stackBelow; 570 stackBelow = new Stack (); 571 } 572 573 generatedXML.sendData(); 574 575 } 576 } 577 578 generatedXML.writeElement(null, "multistatus", 579 XMLWriter.CLOSING); 580 581 generatedXML.sendData(); 582 583 } 584 585 586 589 protected void doProppatch(HttpServletRequest req, 590 HttpServletResponse resp) 591 throws ServletException , IOException { 592 593 if (readOnly) { 594 resp.sendError(WebdavStatus.SC_FORBIDDEN); 595 return; 596 } 597 598 if (isLocked(req)) { 599 resp.sendError(WebdavStatus.SC_LOCKED); 600 return; 601 } 602 603 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 604 605 } 606 607 608 611 protected void doMkcol(HttpServletRequest req, HttpServletResponse resp) 612 throws ServletException , IOException { 613 614 if (readOnly) { 615 resp.sendError(WebdavStatus.SC_FORBIDDEN); 616 return; 617 } 618 619 if (isLocked(req)) { 620 resp.sendError(WebdavStatus.SC_LOCKED); 621 return; 622 } 623 624 String path = getRelativePath(req); 625 626 if ((path.toUpperCase().startsWith("/WEB-INF")) || 627 (path.toUpperCase().startsWith("/META-INF"))) { 628 resp.sendError(WebdavStatus.SC_FORBIDDEN); 629 return; 630 } 631 632 boolean exists = true; 633 Object object = null; 634 try { 635 object = resources.lookup(path); 636 } catch (NamingException e) { 637 exists = false; 638 } 639 640 if (exists) { 643 StringBuffer methodsAllowed = determineMethodsAllowed(resources, 645 req); 646 647 resp.addHeader("Allow", methodsAllowed.toString()); 648 649 resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED); 650 return; 651 } 652 653 if (req.getInputStream().available() > 0) { 654 DocumentBuilder documentBuilder = getDocumentBuilder(); 655 try { 656 Document document = documentBuilder.parse 657 (new InputSource (req.getInputStream())); 658 resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED); 660 return; 661 662 } catch(SAXException saxe) { 663 resp.sendError(WebdavStatus.SC_BAD_REQUEST); 665 return; 666 } 667 } 668 669 boolean result = true; 670 try { 671 resources.createSubcontext(path); 672 } catch (NamingException e) { 673 result = false; 674 } 675 676 if (!result) { 677 resp.sendError(WebdavStatus.SC_CONFLICT, 678 WebdavStatus.getStatusText 679 (WebdavStatus.SC_CONFLICT)); 680 } else { 681 resp.setStatus(WebdavStatus.SC_CREATED); 682 lockNullResources.remove(path); 684 } 685 686 } 687 688 689 692 protected void doDelete(HttpServletRequest req, HttpServletResponse resp) 693 throws ServletException , IOException { 694 695 if (readOnly) { 696 resp.sendError(WebdavStatus.SC_FORBIDDEN); 697 return; 698 } 699 700 if (isLocked(req)) { 701 resp.sendError(WebdavStatus.SC_LOCKED); 702 return; 703 } 704 705 deleteResource(req, resp); 706 707 } 708 709 710 719 protected void doPut(HttpServletRequest req, HttpServletResponse resp) 720 throws ServletException , IOException { 721 722 if (isLocked(req)) { 723 resp.sendError(WebdavStatus.SC_LOCKED); 724 return; 725 } 726 727 super.doPut(req, resp); 728 729 String path = getRelativePath(req); 730 731 lockNullResources.remove(path); 733 734 } 735 736 739 protected void doCopy(HttpServletRequest req, HttpServletResponse resp) 740 throws ServletException , IOException { 741 742 if (readOnly) { 743 resp.sendError(WebdavStatus.SC_FORBIDDEN); 744 return; 745 } 746 747 copyResource(req, resp); 748 749 } 750 751 752 755 protected void doMove(HttpServletRequest req, HttpServletResponse resp) 756 throws ServletException , IOException { 757 758 if (readOnly) { 759 resp.sendError(WebdavStatus.SC_FORBIDDEN); 760 return; 761 } 762 763 &nb
|