You are viewing a plain text version of this content. The canonical link for it is here.
Posted to slide-dev@jakarta.apache.org by re...@locus.apache.org on 2000/09/25 09:24:54 UTC

cvs commit: jakarta-slide/src/share/org/apache/slide/webdav/method GetMethod.java PutMethod.java WebdavMethod.java

remm        00/09/25 00:24:53

  Modified:    src/doc  status.xml
               src/share/org/apache/slide/content ContentImpl.java
                        NodeRevisionDescriptor.java
               src/share/org/apache/slide/lock Lock.java LockImpl.java
                        NodeLock.java
               src/share/org/apache/slide/util/resources
                        messages.properties
               src/share/org/apache/slide/webdav/method GetMethod.java
                        PutMethod.java WebdavMethod.java
  Log:
  - Collection browser in the DAV servlet (based on some Catalina and
    Tomcat code)
  - HTTP/1.1 headers support (should allow resuming, all types of conditional
    requests, multipart requests, ETags, ...)
  - Lock checking are back in the core
  - Added a new easier to use lockCheck method in the lock helper
  - Fixes for the handling of Dates in NodeRevisionDescriptor and in the DAV
    servlet
  - Updated the status.xml page regarding the current progress
  
  Revision  Changes    Path
  1.7       +12 -39    jakarta-slide/src/doc/status.xml
  
  Index: status.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/doc/status.xml,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- status.xml	2000/09/23 07:41:40	1.6
  +++ status.xml	2000/09/25 07:24:36	1.7
  @@ -57,15 +57,12 @@
           <th width="30%">Volunteers</th>
         </tr>
         <tr>
  -        <td align="center">High</td>
  +        <td align="center">Medium</td>
           <td>
  -          Put back security and lock checks (which were removed due to a 
  -          partial rewrite of the API).
  -          [org.apache.slide.structure]
  -          [org.apache.slide.content]
  -          [org.apache.slide.lock]
  +          Add additional helper macros.
  +          [org.apache.slide.macro]
           </td>
  -        <td><a href="mailto:remm@apache.org">Remy Maucherat</a></td>
  +        <td>---</td>
         </tr>
         <tr>
           <td align="center">Low</td>
  @@ -83,15 +80,6 @@
           </td>
           <td><a href="mailto:remm@apache.org">Remy Maucherat</a></td>
         </tr>
  -      <tr>
  -        <td align="center">Low</td>
  -        <td>
  -          Add additional helper macros (for exemple, macros for users / groups
  -          management).
  -          [org.apache.slide.macro]
  -        </td>
  -        <td>---</td>
  -      </tr>
       </table>
       </p>
       
  @@ -128,7 +116,7 @@
           <th width="30%">Volunteers</th>
         </tr>
         <tr>
  -        <td align="center">Medium</td>
  +        <td align="center">Low</td>
           <td>
             Add a filesystem content store without versioning, or add an option
             to the current filesystem content store which disables 
  @@ -137,7 +125,7 @@
           <td><a href="mailto:cvrc@netscape.com">Murthy Chintalapati</a></td>
         </tr>
         <tr>
  -        <td align="center">Medium</td>
  +        <td align="center">Low</td>
           <td>
             Add a limited filesystem descriptors store (without versioning 
             support), to make it possible to have Slide manage existing
  @@ -225,21 +213,6 @@
         <tr>
           <td align="center">Medium</td>
           <td>
  -          Add a nice collection browser (returned when someone does a GET on
  -          a collection object), using Catalina code.
  -        </td>
  -        <td><a href="mailto:remm@apache.org">Remy Maucherat</a></td>
  -      </tr>
  -      <tr>
  -        <td align="center">Medium</td>
  -        <td>
  -          Support HTTP/1.1 headers, using Catalina code.
  -        </td>
  -        <td><a href="mailto:remm@apache.org">Remy Maucherat</a></td>
  -      </tr>
  -      <tr>
  -        <td align="center">Low</td>
  -        <td>
             DAV ACL support (based on the drafts).
           </td>
           <td><a href="mailto:cvrc@netscape.com">Murthy Chintalapati</a></td>
  @@ -283,20 +256,20 @@
         <tr>
           <td align="center">High</td>
           <td>
  -          DAV ACL support.
  +          Write a command line client using the DAV library.
           </td>
           <td>
  +          <a href="mailto:remm@apache.org">Remy Maucherat</a>
  +          <br/>
  +          <a href="mailto:bcholmes@interlog.com">B.C. Holmes</a>
           </td>
         </tr>
         <tr>
  -        <td align="center">High</td>
  +        <td align="center">Medium</td>
           <td>
  -          Write a command line client using the DAV library.
  +          DAV ACL support.
           </td>
           <td>
  -          <a href="mailto:remm@apache.org">Remy Maucherat</a>
  -          <br/>
  -          <a href="mailto:bcholmes@interlog.com">B.C. Holmes</a>
           </td>
         </tr>
       </table>
  
  
  
  1.4       +129 -22   jakarta-slide/src/share/org/apache/slide/content/ContentImpl.java
  
  Index: ContentImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/share/org/apache/slide/content/ContentImpl.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ContentImpl.java	2000/06/14 05:12:14	1.3
  +++ ContentImpl.java	2000/09/25 07:24:39	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/content/ContentImpl.java,v 1.3 2000/06/14 05:12:14 remm Exp $
  - * $Revision: 1.3 $
  - * $Date: 2000/06/14 05:12:14 $
  + * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/content/ContentImpl.java,v 1.4 2000/09/25 07:24:39 remm Exp $
  + * $Revision: 1.4 $
  + * $Date: 2000/09/25 07:24:39 $
    *
    * ====================================================================
    *
  @@ -77,7 +77,7 @@
    * Implementation of the content interface.
    * 
    * @author <a href="mailto:remm@exoffice.com">Remy Maucherat</a>
  - * @version $Revision: 1.3 $
  + * @version $Revision: 1.4 $
    */
   public final class ContentImpl implements Content {
       
  @@ -154,17 +154,12 @@
           ObjectLockedException {
           ObjectNode associatedObject = structureHelper.retrieve(token, strUri);
           
  -        // Check security
  -        // For now, we don't do any security check here (that is, no more 
  -        // than what have already been done by the retrieve object, 
  -        // which checks the readObject action.
  -        // securityHelper.checkCredentials(token, associatedObject, 
  -        // namespaceConfig.getModifyRevisionsAction());
  -            
  -        // Check locking
  -        // We don't perform additional checks for locking either.
  -        // lockHelper.checkLock(token, (SubjectNode) associatedObject, 
  -        // subject, namespaceConfig.getModifyRevisionsAction());
  +        // Checking security and locking
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getReadRevisionMetadataAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getReadRevisionMetadataAction());
           
           Uri objectUri = namespace.getUri(strUri);
           NodeRevisionDescriptors revisionDescriptors = null;
  @@ -196,6 +191,14 @@
           RevisionDescriptorNotFoundException, ObjectLockedException {
           ObjectNode associatedObject = structureHelper
               .retrieve(token, revisionDescriptors.getUri());
  +        
  +        // Checking security and locking
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getReadRevisionMetadataAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getReadRevisionMetadataAction());
  +        
           Uri objectUri = namespace.getUri(revisionDescriptors.getUri());
           return objectUri.getDescriptorsStore()
               .retrieveRevisionDescriptor(objectUri, revisionNumber);
  @@ -250,6 +253,14 @@
           RevisionNotFoundException, RevisionContentNotFoundException, 
           ObjectLockedException {
           ObjectNode associatedObject = structureHelper.retrieve(token, strUri);
  +        
  +        // Checking security and locking
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getReadRevisionContentAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getReadRevisionContentAction());
  +        
           Uri objectUri = namespace.getUri(strUri);
           return objectUri.getContentStore()
               .retrieveRevisionContent(objectUri, 
  @@ -273,9 +284,19 @@
           
           // Retrieve the associated object
           ObjectNode associatedObject = structureHelper.retrieve(token, strUri);
  -        // Next we do a security check and a locking check for modifyRevisions
           
  -        // TODO !
  +        // Next we do a security check and a locking check for modifyRevisions
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionMetadataAction());
  +        lockHelper.checkLock
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionMetadataAction());
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionContentAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getCreateRevisionContentAction());
           
           Uri objectUri = namespace.getUri(strUri);
           
  @@ -358,7 +379,7 @@
                           .retrieveRevisionDescriptor(objectUri, 
                                                       newRevisionNumber);
                       Enumeration newPropertiesList = 
  -                        revisionDescriptor.getPropertiesValues();
  +                        revisionDescriptor.enumerateProperties();
                       while (newPropertiesList.hasMoreElements()) {
                           oldRevisionDescriptor
                               .setProperty( (NodeProperty) newPropertiesList
  @@ -430,9 +451,19 @@
           
           // Retrieve the associated object
           ObjectNode associatedObject = structureHelper.retrieve(token, strUri);
  -        // Next we do a security check and a locking check for modifyRevisions
           
  -        // TODO !
  +        // Next we do a security check and a locking check for modifyRevisions
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionMetadataAction());
  +        lockHelper.checkLock
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionMetadataAction());
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionContentAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getCreateRevisionContentAction());
           
           Uri objectUri = namespace.getUri(strUri);
           
  @@ -523,6 +554,23 @@
           throws ObjectNotFoundException, AccessDeniedException, 
           LinkedObjectNotFoundException, ServiceAccessException, 
           RevisionDescriptorNotFoundException, ObjectLockedException {
  +        
  +        // Retrieve the associated object
  +        ObjectNode associatedObject = structureHelper.retrieve(token, strUri);
  +        
  +        // Next we do a security check and a locking check for modifyRevisions
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionMetadataAction());
  +        lockHelper.checkLock
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionMetadataAction());
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionContentAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getCreateRevisionContentAction());
  +        
           Domain.info("Fork not implemented");
       }
       
  @@ -542,6 +590,23 @@
           throws ObjectNotFoundException, AccessDeniedException, 
           LinkedObjectNotFoundException, ServiceAccessException, 
           RevisionDescriptorNotFoundException, ObjectLockedException {
  +        
  +        // Retrieve the associated object
  +        ObjectNode associatedObject = structureHelper.retrieve(token, strUri);
  +        
  +        // Next we do a security check and a locking check for modifyRevisions
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionMetadataAction());
  +        lockHelper.checkLock
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionMetadataAction());
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getCreateRevisionContentAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getCreateRevisionContentAction());
  +        
           Domain.info("Merge not implemented");
       }
       
  @@ -565,6 +630,19 @@
           // Retrieve the associated object
           ObjectNode associatedObject = structureHelper.retrieve(token, strUri);
           
  +        // Next we do a security check and a locking check for modifyRevisions
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getModifyRevisionMetadataAction());
  +        lockHelper.checkLock
  +            (token, associatedObject, 
  +             namespaceConfig.getModifyRevisionMetadataAction());
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getModifyRevisionContentAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getModifyRevisionContentAction());
  +        
       }
       
       
  @@ -579,7 +657,22 @@
           LinkedObjectNotFoundException, ServiceAccessException, 
           RevisionDescriptorNotFoundException, ObjectLockedException {
           
  -        // TODO : Add security and locking checks
  +        // Retrieve the associated object
  +        ObjectNode associatedObject = 
  +            structureHelper.retrieve(token, revisionDescriptors.getUri());
  +        
  +        // Next we do a security check and a locking check for modifyRevisions
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getRemoveRevisionMetadataAction());
  +        lockHelper.checkLock
  +            (token, associatedObject, 
  +             namespaceConfig.getRemoveRevisionMetadataAction());
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getRemoveRevisionContentAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getRemoveRevisionContentAction());
           
           Uri objectUri = namespace.getUri(revisionDescriptors.getUri());
           
  @@ -614,7 +707,21 @@
           LinkedObjectNotFoundException, ServiceAccessException, 
           RevisionDescriptorNotFoundException, ObjectLockedException {
           
  -        // TODO : Add security and locking checks
  +        // Retrieve the associated object
  +        ObjectNode associatedObject = structureHelper.retrieve(token, strUri);
  +        
  +        // Next we do a security check and a locking check for modifyRevisions
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getRemoveRevisionMetadataAction());
  +        lockHelper.checkLock
  +            (token, associatedObject, 
  +             namespaceConfig.getRemoveRevisionMetadataAction());
  +        securityHelper.checkCredentials
  +            (token, associatedObject, 
  +             namespaceConfig.getRemoveRevisionContentAction());
  +        lockHelper.checkLock(token, associatedObject, 
  +                             namespaceConfig.getRemoveRevisionContentAction());
           
           Uri objectUri = namespace.getUri(strUri);
           
  
  
  
  1.6       +14 -7     jakarta-slide/src/share/org/apache/slide/content/NodeRevisionDescriptor.java
  
  Index: NodeRevisionDescriptor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/share/org/apache/slide/content/NodeRevisionDescriptor.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- NodeRevisionDescriptor.java	2000/09/24 17:20:08	1.5
  +++ NodeRevisionDescriptor.java	2000/09/25 07:24:40	1.6
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/content/NodeRevisionDescriptor.java,v 1.5 2000/09/24 17:20:08 remm Exp $
  - * $Revision: 1.5 $
  - * $Date: 2000/09/24 17:20:08 $
  + * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/content/NodeRevisionDescriptor.java,v 1.6 2000/09/25 07:24:40 remm Exp $
  + * $Revision: 1.6 $
  + * $Date: 2000/09/25 07:24:40 $
    *
    * ====================================================================
    *
  @@ -77,7 +77,7 @@
    * Node Revision Descriptor class.
    * 
    * @author <a href="mailto:remm@exoffice.com">Remy Maucherat</a>
  - * @version $Revision: 1.5 $
  + * @version $Revision: 1.6 $
    */
   public final class NodeRevisionDescriptor implements Serializable, Cloneable {
       
  @@ -582,6 +582,9 @@
           if (creationDate == null) {
               return null;
           } else {
  +            if (creationDate.getValue() instanceof Date) {
  +                return format.format((Date) creationDate.getValue());
  +            }
               return creationDate.getValue().toString();
           }
       }
  @@ -633,7 +636,11 @@
           if (lastModified == null) {
               return null;
           } else {
  -            return lastModified.getValue().toString();
  +            if (lastModified.getValue() instanceof Date) {
  +                return format.format((Date) lastModified.getValue());
  +            } else {
  +                return lastModified.getValue().toString();
  +            }
           }
       }
       
  @@ -726,13 +733,13 @@
       
       private void initDefaultProperties() {
           
  -        setCreationDate(format.format(new Date()));
  +        setCreationDate(new Date());
           setName("");
           // By default, a resource is a collection
           setProperty(TYPE, "<collection/>", true);
           setProperty(SOURCE, "", true);
           setContentLength(0);
  -        setLastModified(format.format(new Date()));
  +        setLastModified(new Date());
           
       }
       
  
  
  
  1.3       +29 -7     jakarta-slide/src/share/org/apache/slide/lock/Lock.java
  
  Index: Lock.java
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/share/org/apache/slide/lock/Lock.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Lock.java	2000/09/05 05:38:09	1.2
  +++ Lock.java	2000/09/25 07:24:44	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/lock/Lock.java,v 1.2 2000/09/05 05:38:09 remm Exp $
  - * $Revision: 1.2 $
  - * $Date: 2000/09/05 05:38:09 $
  + * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/lock/Lock.java,v 1.3 2000/09/25 07:24:44 remm Exp $
  + * $Revision: 1.3 $
  + * $Date: 2000/09/25 07:24:44 $
    *
    * ====================================================================
    *
  @@ -78,7 +78,7 @@
    * Lock helper class.
    * 
    * @author <a href="mailto:remm@exoffice.com">Remy Maucherat</a>
  - * @version $Revision: 1.2 $
  + * @version $Revision: 1.3 $
    */
   public interface Lock {
       
  @@ -232,6 +232,28 @@
       
       
       /**
  +     * Utility function for lock checking. Mirrors Security.checkCredentials.
  +     * 
  +     * @param credentialsToken Credetials token
  +     * @param subject Object on which the action is performed
  +     * @param action Action to test
  +     * @exception ObjectIsLockedException Can't perform specified action 
  +     * on object
  +     * @exception ServiceConnectionFailedException Connection to Lock Store 
  +     * service failed
  +     * @exception ServiceAccessException Low level service access exception
  +     * @exception ObjectNotFoundException One of the objects referenced 
  +     * in the lock token were not found
  +     * @exception LinkedObjectNotFoundException Broken link encountered 
  +     * during object retrieval
  +     */
  +    void checkLock(CredentialsToken credentialsToken, ObjectNode object, 
  +                   ActionNode action)
  +        throws ServiceAccessException, ObjectNotFoundException, 
  +        ObjectLockedException;
  +    
  +    
  +    /**
        * Check locking for a specific action and credential.
        * 
        * @param credentialsToken Credetials token
  @@ -248,7 +270,7 @@
        * @exception LinkedObjectNotFoundException Broken link encountered 
        * during object retrieval
        */
  -    void checkLock(CredentialsToken credentialsToken, SubjectNode subject, 
  +    void checkLock(CredentialsToken credentialsToken, ObjectNode subject, 
                      SubjectNode user, ActionNode action)
           throws ServiceConnectionFailedException, ServiceAccessException, 
           ObjectNotFoundException, LinkedObjectNotFoundException, 
  @@ -271,7 +293,7 @@
        * @exception LinkedObjectNotFoundException Broken link encountered 
        * during object retrieval
        */
  -    boolean isLocked(CredentialsToken credentialsToken, SubjectNode subject, 
  +    boolean isLocked(CredentialsToken credentialsToken, ObjectNode subject, 
                        SubjectNode user, ActionNode action, boolean tryToLock)
           throws ServiceConnectionFailedException, ServiceAccessException, 
           ObjectNotFoundException, LinkedObjectNotFoundException;
  @@ -298,7 +320,7 @@
        * @exception LinkedObjectNotFoundException Broken link encountered 
        * during object retrieval
        */
  -    boolean isLocked(CredentialsToken credentialsToken, SubjectNode subject, 
  +    boolean isLocked(CredentialsToken credentialsToken, ObjectNode subject, 
                        SubjectNode user, ActionNode action, boolean inheritance,
                        boolean tryToLock)
           throws ServiceConnectionFailedException, ServiceAccessException, 
  
  
  
  1.5       +56 -46    jakarta-slide/src/share/org/apache/slide/lock/LockImpl.java
  
  Index: LockImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/share/org/apache/slide/lock/LockImpl.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- LockImpl.java	2000/09/21 22:46:31	1.4
  +++ LockImpl.java	2000/09/25 07:24:44	1.5
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/lock/LockImpl.java,v 1.4 2000/09/21 22:46:31 remm Exp $
  - * $Revision: 1.4 $
  - * $Date: 2000/09/21 22:46:31 $
  + * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/lock/LockImpl.java,v 1.5 2000/09/25 07:24:44 remm Exp $
  + * $Revision: 1.5 $
  + * $Date: 2000/09/25 07:24:44 $
    *
    * ====================================================================
    *
  @@ -78,7 +78,7 @@
    * Lock helper class.
    * 
    * @author <a href="mailto:remm@exoffice.com">Remy Maucherat</a>
  - * @version $Revision: 1.4 $
  + * @version $Revision: 1.5 $
    */
   public final class LockImpl implements Lock {
       
  @@ -357,6 +357,39 @@
       
       
       /**
  +     * Utility function for lock checking. Mirrors Security.checkCredentials.
  +     * 
  +     * @param token Credetials token
  +     * @param subject Object on which the action is performed
  +     * @param action Action to test
  +     * @exception ObjectIsLockedException Can't perform specified action 
  +     * on object
  +     * @exception ServiceAccessException Low level service access exception
  +     * @exception ObjectNotFoundException One of the objects referenced 
  +     * in the lock token were not found
  +     */
  +    public void checkLock(CredentialsToken token, 
  +                          ObjectNode object, ActionNode action)
  +        throws ServiceAccessException, ObjectNotFoundException, 
  +        ObjectLockedException {
  +        
  +        if (Configuration.useIntegratedLocking()) {
  +            
  +            Uri objectUri = namespace.getUri(object.getUri());
  +            ObjectNode realObject = objectUri.getDescriptorsStore()
  +                .retrieveObject(objectUri);
  +            Uri subjectUri = null;
  +            subjectUri = namespace.getUri(namespaceConfig.getUsersPath() + "/"
  +                                          + token.getPublicCredentials());
  +            SubjectNode subject = (SubjectNode) subjectUri
  +                .getDescriptorsStore().retrieveObject(subjectUri);
  +            checkLock(token, realObject, subject, action);
  +            
  +        }
  +    }
  +    
  +    
  +    /**
        * Check locking for a specific action and credential.
        * 
        * @param credentialsToken Credetials token
  @@ -365,19 +398,14 @@
        * @param action Action to test
        * @exception ObjectIsLockedException Can't perform specified action 
        * on object
  -     * @exception ServiceConnectionFailedException Connection to 
  -     * Lock Store service failed
        * @exception ServiceAccessException Low level service access exception
        * @exception ObjectNotFoundException One of the objects referenced 
        * in the lock token were not found
  -     * @exception LinkedObjectNotFoundException Broken link encountered 
  -     * during object retrieval
        */
       public void checkLock(CredentialsToken credentialsToken, 
  -                          SubjectNode subject, SubjectNode user, 
  +                          ObjectNode subject, SubjectNode user, 
                             ActionNode action)
  -        throws ServiceConnectionFailedException, ServiceAccessException, 
  -        ObjectNotFoundException, LinkedObjectNotFoundException, 
  +        throws ServiceAccessException, ObjectNotFoundException, 
           ObjectLockedException {
           
           if (Configuration.useIntegratedLocking()) {
  @@ -397,19 +425,14 @@
        * @param user User to test
        * @param action Locked for action
        * @return boolean True if the subject is locked
  -     * @exception ServiceConnectionFailedException Connection to Lock Store 
  -     * service failed
        * @exception ServiceAccessException Low level service access exception
        * @exception ObjectNotFoundException One of the objects referenced 
        * in the lock token were not found
  -     * @exception LinkedObjectNotFoundException Broken link encountered 
  -     * during object retrieval
        */
       public boolean isLocked(CredentialsToken credentialsToken, 
  -                            SubjectNode subject, SubjectNode user, 
  +                            ObjectNode subject, SubjectNode user, 
                               ActionNode action, boolean tryToLock)
  -        throws ServiceConnectionFailedException, ServiceAccessException, 
  -        ObjectNotFoundException, LinkedObjectNotFoundException {
  +        throws ServiceAccessException, ObjectNotFoundException {
           NodeLock token = new NodeLock(subject, user, action, 
                                         new Date(), false);
           return isLocked(credentialsToken, token, tryToLock);
  @@ -428,21 +451,15 @@
        * @param tryToLock True if the check is intended to check whether 
        * or not we can put a new lock
        * @return boolean True if the subject is locked
  -     * @exception ServiceConnectionFailedException Connection to 
  -     * Lock Store service failed
        * @exception ServiceAccessException Low level service access exception
        * @exception ObjectNotFoundException One of the objects referenced 
        * in the lock token were not found
  -     * @exception LinkedObjectNotFoundException Broken link encountered 
  -     * during object retrieval
        */
       public boolean isLocked(CredentialsToken credentialsToken, 
  -                            SubjectNode subject, SubjectNode user, 
  +                            ObjectNode subject, SubjectNode user, 
                               ActionNode action, boolean inheritance, 
                               boolean tryToLock)
  -        throws ServiceConnectionFailedException, ServiceAccessException, 
  -        ObjectNotFoundException, 
  -        LinkedObjectNotFoundException {
  +        throws ServiceAccessException, ObjectNotFoundException {
           NodeLock token = new NodeLock(subject, user, action, new Date(), 
                                         inheritance);
           return isLocked(credentialsToken, token, tryToLock);
  @@ -454,23 +471,18 @@
        * 
        * @param token Lock token to test
        * @return boolean True if locked
  -     * @exception ServiceConnectionFailedException Connection to Lock Store 
  -     * service failed
        * @exception ServiceAccessException Low level service access exception
        * @exception ObjectNotFoundException One of the objects referenced 
        * in the lock token were not found
  -     * @exception LinkedObjectNotFoundException Broken link encountered 
  -     * during object retrieval
        */
       public boolean isLocked(CredentialsToken credentialsToken, NodeLock token,
                               boolean tryToLock)
  -        throws ServiceConnectionFailedException, ServiceAccessException, 
  -        ObjectNotFoundException, LinkedObjectNotFoundException {
  +        throws ServiceAccessException, ObjectNotFoundException {
           
  -        Uri subjectUri = namespace.getUri(token.getObjectUri());
  -        SubjectNode initialSubject = (SubjectNode) subjectUri
  -            .getDescriptorsStore().retrieveObject(subjectUri);
  -        Enumeration scopes = subjectUri.getScopes();
  +        Uri objectUri = namespace.getUri(token.getObjectUri());
  +        ObjectNode initialObject = objectUri.getDescriptorsStore()
  +            .retrieveObject(objectUri);
  +        Enumeration scopes = objectUri.getScopes();
           
           // At the end of the test, this boolean's value is true if we can
           // actually put the lock on the desired subject.
  @@ -499,14 +511,14 @@
           // locked with an incompatible lock.
           if (token.isInheritable()) {
               Stack childrenStack = new Stack();
  -            childrenStack.push(initialSubject);
  +            childrenStack.push(initialObject);
               while (!isLocked && !childrenStack.empty()) {
  -                SubjectNode currentSubject = (SubjectNode) childrenStack.pop();
  -                Uri currentSubjectUri = 
  -                    namespace.getUri(currentSubject.getUri());
  +                ObjectNode currentObject = (ObjectNode) childrenStack.pop();
  +                Uri currentObjectUri = 
  +                    namespace.getUri(currentObject.getUri());
                   // We test the compatibility of the child
  -                Enumeration locks = currentSubjectUri.getDescriptorsStore()
  -                    .enumerateLocks(currentSubjectUri);
  +                Enumeration locks = currentObjectUri.getDescriptorsStore()
  +                    .enumerateLocks(currentObjectUri);
                   
                   while (locks.hasMoreElements()) {
                       NodeLock currentLockToken = (NodeLock) locks.nextElement();
  @@ -519,7 +531,7 @@
                   // We get the children and add them to the Stack.
                   
                   Vector childrenVector = new Vector();
  -                Enumeration childrenUri = currentSubject.enumerateChildren();
  +                Enumeration childrenUri = currentObject.enumerateChildren();
                   while (childrenUri.hasMoreElements()) {
                       String childUri = (String) childrenUri.nextElement();
                       Uri tempUri = namespace.getUri(childUri);
  @@ -532,9 +544,7 @@
                   while (children.hasMoreElements()) {
                       ObjectNode tempObject = 
                           (ObjectNode) children.nextElement();
  -                    if (tempObject instanceof SubjectNode) {
  -                        childrenStack.push(tempObject);
  -                    }
  +                    childrenStack.push(tempObject);
                   }
               }
           }
  
  
  
  1.4       +6 -6      jakarta-slide/src/share/org/apache/slide/lock/NodeLock.java
  
  Index: NodeLock.java
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/share/org/apache/slide/lock/NodeLock.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- NodeLock.java	2000/09/20 06:06:15	1.3
  +++ NodeLock.java	2000/09/25 07:24:45	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/lock/NodeLock.java,v 1.3 2000/09/20 06:06:15 remm Exp $
  - * $Revision: 1.3 $
  - * $Date: 2000/09/20 06:06:15 $
  + * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/lock/NodeLock.java,v 1.4 2000/09/25 07:24:45 remm Exp $
  + * $Revision: 1.4 $
  + * $Date: 2000/09/25 07:24:45 $
    *
    * ====================================================================
    *
  @@ -74,7 +74,7 @@
    * NodeLock class.
    * 
    * @author <a href="mailto:remm@exoffice.com">Remy Maucherat</a>
  - * @version $Revision: 1.3 $
  + * @version $Revision: 1.4 $
    */
   public final class NodeLock implements Cloneable {
       
  @@ -98,7 +98,7 @@
        * @param expirationDate Date of expiration of the lock
        * @param inheritance True if lock is inheritable
        */
  -    public NodeLock(SubjectNode locked, SubjectNode user, ActionNode lockType,
  +    public NodeLock(ObjectNode locked, SubjectNode user, ActionNode lockType,
                       Date expirationDate, boolean inheritance) {
           this(locked.getUri(), user.getUri(), lockType.getUri(), expirationDate,
                inheritance);
  @@ -114,7 +114,7 @@
        * @param expirationDate Date of expiration of the lock
        * @param inheritance True if lock is inheritable
        */
  -    public NodeLock(SubjectNode locked, SubjectNode user, ActionNode lockType,
  +    public NodeLock(ObjectNode locked, SubjectNode user, ActionNode lockType,
                       Date expirationDate, boolean inheritance, 
                       boolean exclusive) {
           this(locked.getUri(), user.getUri(), lockType.getUri(), expirationDate,
  
  
  
  1.3       +13 -4     jakarta-slide/src/share/org/apache/slide/util/resources/messages.properties
  
  Index: messages.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/share/org/apache/slide/util/resources/messages.properties,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- messages.properties	2000/08/02 04:28:11	1.2
  +++ messages.properties	2000/09/25 07:24:48	1.3
  @@ -1,9 +1,9 @@
   #
   # Slide messages
   #
  -# $Id: messages.properties,v 1.2 2000/08/02 04:28:11 remm Exp $
  +# $Id: messages.properties,v 1.3 2000/09/25 07:24:48 remm Exp $
   #
  -# @version $Revision: 1.2 $ $Date: 2000/08/02 04:28:11 $
  +# @version $Revision: 1.3 $ $Date: 2000/09/25 07:24:48 $
   #
   
   #
  @@ -99,8 +99,17 @@
   # Messages produced by webdav
   #
   org.apache.slide.webdav.GetMethod.directorylistingfor=\
  -        Directory listing for 
  -
  +        Directory listing for {0}
  +org.apache.slide.webdav.GetMethod.filename=\
  +	Filename
  +org.apache.slide.webdav.GetMethod.size=\
  +	Size
  +org.apache.slide.webdav.GetMethod.parent=\
  +	Up To {0}
  +org.apache.slide.webdav.GetMethod.lastModified=\
  +	Last Modified
  +org.apache.slide.webdav.GetMethod.version=\
  +	Jakarta Slide 1.0
   
   #
   # Messages produced by org.apache.slide.util.Configuration
  
  
  
  1.4       +937 -49   jakarta-slide/src/share/org/apache/slide/webdav/method/GetMethod.java
  
  Index: GetMethod.java
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/share/org/apache/slide/webdav/method/GetMethod.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- GetMethod.java	2000/09/02 03:13:32	1.3
  +++ GetMethod.java	2000/09/25 07:24:50	1.4
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/webdav/method/GetMethod.java,v 1.3 2000/09/02 03:13:32 remm Exp $
  - * $Revision: 1.3 $
  - * $Date: 2000/09/02 03:13:32 $
  + * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/webdav/method/GetMethod.java,v 1.4 2000/09/25 07:24:50 remm Exp $
  + * $Revision: 1.4 $
  + * $Date: 2000/09/25 07:24:50 $
    *
    * ====================================================================
    *
  @@ -66,12 +66,15 @@
   import java.security.Principal;
   import java.io.*;
   import java.util.*;
  +import java.text.SimpleDateFormat;
  +import java.text.ParseException;
   import javax.servlet.*;
   import javax.servlet.http.*;
   import org.apache.slide.common.*;
   import org.apache.slide.security.AccessDeniedException;
   import org.apache.slide.webdav.common.*;
   import org.apache.slide.webdav.*;
  +import org.apache.slide.util.Messages;
   import org.apache.slide.content.*;
   import org.apache.slide.lock.*;
   import org.apache.slide.structure.*;
  @@ -88,6 +91,40 @@
       protected final int BUFFER_SIZE = 2048;
       
       
  +    /**
  +     * The set of SimpleDateFormat formats to use in getDateHeader().
  +     */
  +    protected static final SimpleDateFormat formats[] = {
  +        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
  +        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
  +        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
  +    };
  +    
  +    
  +    /**
  +     * MIME multipart separation string
  +     */
  +    protected static final String mimeSeparation = "SLIDE_MIME_BOUNDARY";
  +    
  +    
  +    /**
  +     * The input buffer size to use when serving resources.
  +     */
  +    protected int input = 2048;
  +    
  +    
  +    /**
  +     * The output buffer size to use when serving resources.
  +     */
  +    protected int output = 2048;
  +    
  +    
  +    /**
  +     * Print content.
  +     */
  +    protected boolean printContent = true;
  +    
  +    
       // ----------------------------------------------------- Instance Variables
       
       
  @@ -145,53 +182,104 @@
                   content.retrieve(credToken, resourcePath);
               
               if (revisionDescriptors.hasRevisions()) {
  +                
                   // Retrieve latest revision descriptor
                   NodeRevisionDescriptor revisionDescriptor = 
                       content.retrieve(credToken, revisionDescriptors);
  -                if (WebdavUtil.isCollection(revisionDescriptor)) {
  +                
  +                if (isCollection(revisionDescriptor)) {
  +                    
                       Writer writer = resp.getWriter();
                       displayDirectoryBrowsing(object, writer);
                       writer.close();
  +                    
                   } else {
  -                    OutputStream os = resp.getOutputStream();
  +                    
  +                    ResourceInfo resourceInfo = 
  +                        new ResourceInfo(resourcePath, revisionDescriptor);
  +                    
  +                    // Checking If headers
  +                    if (!checkIfHeaders(req, resp, resourceInfo))
  +                        return;
  +                    
  +                    ServletOutputStream os = resp.getOutputStream();
                       NodeRevisionContent revisionContent = 
                           content.retrieve(credToken, revisionDescriptors, 
                                            revisionDescriptor);
  -                    resp.setStatus(WebdavStatus.SC_OK);
  -                    resp.setContentLength
  -                        ((int) revisionDescriptor.getContentLength());
  -                    //addHeader("Content-Length", 
  -                    //(new Long(revisionDescriptor.getContentLength()))
  -                    //.toString());
  +                    InputStream is = revisionContent.streamContent();
  +                    
  +                    Vector ranges = parseRange(req, resp, resourceInfo);
  +                    
  +                    // ETag header
  +                    resp.setHeader("ETag", getETag(resourceInfo, true));
                       resp.addHeader
                           ("Last-Modified", 
                            revisionDescriptor.getCreationDate().toString());
  -                    resp.addHeader("Content-Disposition", 
  -                                   "inline; filename=" 
  -                                   + revisionDescriptors.getUri());
  -                    resp.setContentType(revisionDescriptor.getContentType());
                       
  -                    // Writing content to the output stream
  -                    InputStream is = revisionContent.streamContent();
  -                    byte[] buffer = new byte[BUFFER_SIZE];
  -                    long contentLength = revisionDescriptor.getContentLength();
  -                    long position = 0;
  -                    int nChar = 0;
  -                    while (position < contentLength) {
  -                        nChar = is.read(buffer, 0, BUFFER_SIZE);
  -                        if (nChar == -1) {
  -                            break;
  +                    if ( ((ranges == null) || (ranges.isEmpty())) 
  +                         && (req.getHeader("Range") == null) ) {
  +                        
  +                        resp.setContentType
  +                            (revisionDescriptor.getContentType());
  +                        resp.setContentLength
  +                            ((int) revisionDescriptor.getContentLength());
  +                        
  +                        // Copy the input stream to our output stream 
  +                        // (if requested)
  +                        if (printContent) {
  +                            resp.setBufferSize(output);
  +                            copy(resourceInfo, is, os);
                           }
  -                        os.write(buffer, 0, nChar);
  -                        position = position + nChar;
  +                        
  +                    } else {
  +                        
  +                        if ((ranges == null) || (ranges.isEmpty()))
  +                            return;
  +                        
  +                        // Partial content response.
  +                        
  +                        resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
  +                        
  +                        if (ranges.size() == 1) {
  +                            
  +                            Range range = (Range) ranges.elementAt(0);
  +                            resp.addHeader("Content-Range", "bytes " 
  +                                           + range.start
  +                                           + "-" + range.end + "/" 
  +                                           + range.length);
  +                            resp.setContentType
  +                                (revisionDescriptor.getContentType());
  +                            
  +                            if (printContent) {
  +                                resp.setBufferSize(output);
  +                                copy(resourceInfo, is, os, range);
  +                            }
  +                            
  +                        } else {
  +                            
  +                            resp.setContentType
  +                                ("multipart/byteranges; boundary="
  +                                 + mimeSeparation);
  +                            
  +                            if (printContent) {
  +                                resp.setBufferSize(output);
  +                                copy(resourceInfo, is, os, 
  +                                     ranges.elements(), 
  +                                     revisionDescriptor.getContentType());
  +                            }
  +                            
  +                        }
  +                        
                       }
  -                    is.close();
  -                    os.close();
  +                    
                   }
  +                
               } else {
  +                
                   Writer writer = resp.getWriter();
                   displayDirectoryBrowsing(object, writer);
                   writer.close();
  +                
               }
               
           } catch (RevisionNotFoundException e) {
  @@ -243,29 +331,829 @@
       
       /**
        * Display a directory browsing page.
  +     */
  +    protected void displayDirectoryBrowsing(ObjectNode object, 
  +                                            Writer servletWriter) 
  +        throws IOException {
  +        
  +        String contextPath = req.getContextPath();
  +        if (contextPath == null)
  +            contextPath = "";
  +        
  +        String name = object.getUri();
  +        
  +        // Number of characters to trim from the beginnings of filenames
  +        int trim = name.length();
  +        if (!name.endsWith("/"))
  +            trim += 1;
  +        if (name.equals("/"))
  +            trim = 1;
  +        
  +        PrintWriter writer = new PrintWriter(servletWriter);
  +        
  +        // Render the page header
  +        writer.print("<html>\r\n");
  +        writer.print("<head>\r\n");
  +        writer.print("<title>");
  +        writer.print
  +            (Messages.format
  +             ("org.apache.slide.webdav.GetMethod.directorylistingfor", name));
  +        writer.print("</title>\r\n</head>\r\n");
  +        writer.print("<body bgcolor=\"white\">\r\n");
  +        writer.print("<table width=\"90%\" cellspacing=\"0\"" +
  +                     " cellpadding=\"5\" align=\"center\">\r\n");
  +        
  +        // Render the in-page title
  +        writer.print("<tr><td colspan=\"3\"><font size=\"+2\">\r\n<strong>");
  +        writer.print
  +            (Messages.format
  +             ("org.apache.slide.webdav.GetMethod.directorylistingfor", name));
  +        writer.print("</strong>\r\n</font></td></tr>\r\n");
  +        
  +        // Render the link to our parent (if required)
  +        String parentDirectory = name;
  +        if (parentDirectory.endsWith("/")) {
  +            parentDirectory = 
  +                parentDirectory.substring(0, parentDirectory.length() - 1);
  +        }
  +        int slash = parentDirectory.lastIndexOf("/");
  +        if (slash >= 0) {
  +            String parent = name.substring(0, slash);
  +            writer.print("<tr><td colspan=\"3\" bgcolor=\"#ffffff\">\r\n");
  +            writer.print("<a href=\"");
  +            writer.print(rewriteUrl(contextPath));
  +            if (parent.equals(""))
  +                parent = "/";
  +            writer.print(parent);
  +            writer.print("\">");
  +            writer.print(Messages.format
  +                         ("org.apache.slide.webdav.GetMethod.parent", parent));
  +            writer.print("</a>\r\n");
  +            writer.print("</td></tr>\r\n");
  +        }
  +        
  +        // Render the column headings
  +        writer.print("<tr bgcolor=\"#cccccc\">\r\n");
  +        writer.print("<td align=\"left\"><font size=\"+1\"><strong>");
  +        writer.print(Messages.message
  +                     ("org.apache.slide.webdav.GetMethod.filename"));
  +        writer.print("</strong></font></td>\r\n");
  +        writer.print("<td align=\"center\"><font size=\"+1\"><strong>");
  +        writer.print(Messages.message
  +                     ("org.apache.slide.webdav.GetMethod.size"));
  +        writer.print("</strong></font></td>\r\n");
  +        writer.print("<td align=\"right\"><font size=\"+1\"><strong>");
  +        writer.print(Messages.message
  +                     ("org.apache.slide.webdav.GetMethod.lastModified"));
  +        writer.print("</strong></font></td>\r\n");
  +        writer.print("</tr>\r\n");
  +        
  +        Enumeration resources = object.enumerateChildren();
  +        boolean shade = false;
  +        
  +        while (resources.hasMoreElements()) {
  +            
  +            String currentResource = (String) resources.nextElement();
  +            
  +            NodeRevisionDescriptor currentDescriptor = null;
  +            
  +            try {
  +                
  +                NodeRevisionDescriptors revisionDescriptors = 
  +                    content.retrieve(credToken, currentResource);
  +                // Retrieve latest revision descriptor
  +                currentDescriptor = 
  +                    content.retrieve(credToken, revisionDescriptors);
  +                
  +            } catch (SlideException e) {
  +                // Debug
  +                e.printStackTrace();
  +            }
  +            
  +            String trimmed = currentResource.substring(trim);
  +            if (trimmed.equalsIgnoreCase("WEB-INF") ||
  +                trimmed.equalsIgnoreCase("META-INF"))
  +                continue;
  +            
  +            writer.print("<tr");
  +            if (shade)
  +                writer.print(" bgcolor=\"eeeeee\"");
  +            writer.print(">\r\n");
  +            shade = !shade;
  +            
  +            writer.print("<td align=\"left\">&nbsp;&nbsp;\r\n");
  +            writer.print("<a href=\"");
  +            writer.print(rewriteUrl(contextPath));
  +            writer.print(rewriteUrl(currentResource));
  +            writer.print("\"><tt>");
  +            writer.print(trimmed);
  +            if (isCollection(currentDescriptor)) {
  +                writer.print("/");
  +            }
  +            writer.print("</tt></a></td>\r\n");
  +            
  +            writer.print("<td align=\"right\"><tt>");
  +            if (isCollection(currentDescriptor))
  +                writer.print("&nbsp;");
  +            else
  +                writer.print(renderSize(currentDescriptor.getContentLength()));
  +            writer.print("</tt></td>\r\n");
  +            
  +            writer.print("<td align=\"right\"><tt>");
  +            if (currentDescriptor != null)
  +                writer.print(currentDescriptor.getLastModified());
  +            writer.print("</tt></td>\r\n");
  +            
  +            writer.print("</tr>\r\n");
  +        }
  +        
  +        
  +        // Render the page footer
  +        writer.print("<tr><td colspan=\"3\">&nbsp;</td></tr>\r\n");
  +        writer.print("<tr><td colspan=\"3\" bgcolor=\"#cccccc\">");
  +        writer.print("<font size=\"-1\">");
  +        writer.print(Messages.message
  +                     ("org.apache.slide.webdav.GetMethod.version"));
  +        writer.print("</font></td></tr>\r\n");
  +        writer.print("</table>\r\n");
  +        writer.print("</body>\r\n");
  +        writer.print("</html>\r\n");
  +        
  +        // Return an input stream to the underlying bytes
  +        writer.flush();
  +        
  +    }
  +    
  +    
  +    // -------------------------------------------------------- Private Methods
  +    
  +    
  +    /**
  +     * Render the specified file size (in bytes).
  +     *
  +     * @param size File size (in bytes)
  +     */
  +    private String renderSize(long size) {
  +        
  +        long leftSide = size / 1024;
  +        long rightSide = (size % 1024) / 103;	// Makes 1 digit
  +        if ((leftSide == 0) && (rightSide == 0) && (size > 0))
  +            rightSide = 1;
  +        
  +        return ("" + leftSide + "." + rightSide + " kb");
  +        
  +    }
  +    
  +    
  +    /**
  +     * URL rewriter.
  +     * 
  +     * @param path Path which has to be rewiten
        */
  -    protected void displayDirectoryBrowsing(ObjectNode object, Writer writer) 
  +    private String rewriteUrl(String path) {
  +        
  +        String normalized = path;
  +        
  +        // Replace " " with "%20"
  +        while (true) {
  +            int index = normalized.indexOf(" ");
  +            if (index < 0)
  +                break;
  +            normalized = normalized.substring(0, index) + "%20"
  +                + normalized.substring(index + 1);
  +        }
  +        
  +        return normalized;
  +        
  +    }
  +    
  +    
  +    /**
  +     * Check if the conditions specified in the optional If headers are 
  +     * satisfied.
  +     * 
  +     * @param request The servlet request we are processing
  +     * @param response The servlet response we are creating
  +     * @param resourceInfo File object
  +     * @return boolean true if the resource meets all the specified conditions,
  +     * and false if any of the conditions is not satisfied, in which case
  +     * request processing is stopped
  +     */
  +    private boolean checkIfHeaders(HttpServletRequest request,
  +                                     HttpServletResponse response, 
  +                                     ResourceInfo resourceInfo)
           throws IOException {
  -        // We display a directory browsing page
  -        resp.setContentType("text/html");
  -        String host = "http://" + req.getServerName() + ":" 
  -            + req.getServerPort();
  -        if (req.getPathInfo() != null) {
  -            host = host + req.getServletPath();
  -        }
  -        writer.write("<!doctype html public \"-//w3c//dtd html 4.0" 
  -                     + "transitional//en\">\n");
  -        writer.write("<html><head><title>" + object.getUri() 
  -                     + "</title></head>");
  -        writer.write("<body bgcolor=white>");
  -        writer.write("<h1>Collection " + object.getUri() + "</h1><hr><p>");
  -        Enumeration childrenList = object.enumerateChildren();
  -        while (childrenList.hasMoreElements()) {
  -            String childUri = (String) childrenList.nextElement();
  -            writer.write("<a href=\"" + host + childUri + "\">" + childUri 
  -                         + "</a><br>");
  +        
  +        String eTag = getETag(resourceInfo, true);
  +        long fileLength = resourceInfo.length;
  +        long lastModified = resourceInfo.date;
  +        
  +        StringTokenizer commaTokenizer;
  +        
  +        String headerValue;
  +        
  +        // Checking If-Match
  +        headerValue = request.getHeader("If-Match");
  +        if (headerValue != null) {
  +            if (headerValue.indexOf("*") == -1) {
  +                
  +                commaTokenizer = new StringTokenizer(headerValue, ",");
  +                boolean conditionSatisfied = false;
  +                
  +                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
  +                    String currentToken = commaTokenizer.nextToken();
  +                    if (currentToken.trim().equals(eTag))
  +                        conditionSatisfied = true;
  +                }
  +                
  +                // If none of the given ETags match, 412 Precodition failed is
  +                // sent back
  +                if (!conditionSatisfied) {
  +                    response.sendError
  +                        (HttpServletResponse.SC_PRECONDITION_FAILED);
  +                    return false;
  +                }
  +                
  +            }
  +        }
  +        
  +        // Checking If-Modified-Since
  +        headerValue = request.getHeader("If-Modified-Since");
  +        if (headerValue != null) {
  +            
  +            // If an If-None-Match header has been specified, if modified since
  +            // is ignored.
  +            if (request.getHeader("If-None-Match") == null) {
  +                
  +                Date date = null;
  +                
  +                // Parsing the HTTP Date
  +                for (int i = 0; (date == null) && (i < formats.length); i++) {
  +                    try {
  +                        date = formats[i].parse(headerValue);
  +                    } catch (ParseException e) {
  +                        ;
  +                    }
  +                }
  +                
  +                if ((date != null) 
  +                    && (lastModified <= (date.getTime() + 1000)) ) {
  +                    // The entity has not been modified since the date 
  +                    // specified by the client. This is not an error case.
  +                    response.sendError
  +                        (HttpServletResponse.SC_NOT_MODIFIED);
  +                    return false;
  +                }
  +                
  +            }
  +            
  +        }
  +        
  +        // Checking If-None-Match
  +        headerValue = request.getHeader("If-None-Match");
  +        if (headerValue != null) {
  +            if (headerValue.indexOf("*") == -1) {
  +                
  +                commaTokenizer = new StringTokenizer(headerValue, ",");
  +                boolean conditionSatisfied = false;
  +                
  +                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
  +                    String currentToken = commaTokenizer.nextToken();
  +                    if (currentToken.trim().equals(eTag))
  +                        conditionSatisfied = true;
  +                }
  +                
  +                if (conditionSatisfied) {
  +                    
  +                    // For GET and HEAD, we should respond with 
  +                    // 304 Not Modified.
  +                    // For every other method, 412 Precondition Failed is sent
  +                    // back.
  +                    if ( ("GET".equals(request.getMethod()))
  +                         || ("HEAD".equals(request.getMethod())) ) {
  +                        response.sendError
  +                            (HttpServletResponse.SC_NOT_MODIFIED);
  +                        return false;
  +                    } else {
  +                        response.sendError
  +                            (HttpServletResponse.SC_PRECONDITION_FAILED);
  +                        return false;
  +                    }
  +                }
  +                
  +            } else {
  +                if (resourceInfo.exists()) {
  +                    
  +                }
  +            }
  +        }
  +        
  +        // Checking If-Unmodified-Since
  +        headerValue = request.getHeader("If-Unmodified-Since");
  +        if (headerValue != null) {
  +            
  +            Date date = null;
  +            
  +            // Parsing the HTTP Date
  +            for (int i = 0; (date == null) && (i < formats.length); i++) {
  +                try {
  +                    date = formats[i].parse(headerValue);
  +                } catch (ParseException e) {
  +                    ;
  +                }
  +            }
  +            
  +            if ( (date != null) && (lastModified > date.getTime()) ) {
  +                // The entity has not been modified since the date 
  +                // specified by the client. This is not an error case.
  +                response.sendError
  +                    (HttpServletResponse.SC_PRECONDITION_FAILED);
  +                return false;
  +            }
  +            
           }
  -        writer.write("</body></html>");
  +        
  +        return true;
       }
  +
  +
  +    /**
  +     * Get the ETag value associated with a file.
  +     * 
  +     * @param resourceInfo File object
  +     * @param strong True if we want a strong ETag, in which case a checksum
  +     * of the file has to be calculated
  +     */
  +    private String getETagValue(ResourceInfo resourceInfo, boolean strong) {
  +        // FIXME : Compute a strong ETag if requested, using an MD5 digest
  +        // of the file contents
  +        return resourceInfo.length + "-" + resourceInfo.date;
  +    }
  +
  +
  +    /**
  +     * Get the ETag associated with a file.
  +     * 
  +     * @param resourceInfo File object
  +     * @param strong True if we want a strong ETag, in which case a checksum
  +     * of the file has to be calculated
  +     */
  +    private String getETag(ResourceInfo resourceInfo, boolean strong) {
  +        if (strong)
  +            return "\"" + getETagValue(resourceInfo, strong) + "\"";
  +        else
  +            return "W/\"" + getETagValue(resourceInfo, strong) + "\"";
  +    }
  +
  +
  +    /**
  +     * Copy the contents of the specified input stream to the specified
  +     * output stream, and ensure that both streams are closed before returning
  +     * (even in the face of an exception).
  +     *
  +     * @param istream The input stream to read from
  +     * @param ostream The output stream to write to
  +     *
  +     * @exception IOException if an input/output error occurs
  +     */
  +    private void copy(ResourceInfo resourceInfo, 
  +                      InputStream resourceInputStream, 
  +                      ServletOutputStream ostream)
  +	throws IOException {
  +
  +        IOException exception = null;
  +            
  +        InputStream istream = new BufferedInputStream
  +            (resourceInputStream, input);
  +        
  +        // Copy the input stream to the output stream
  +        exception = copyRange(istream, ostream);
  +        
  +        // Clean up the input and output streams
  +        try {
  +            istream.close();
  +        } catch (Throwable t) {
  +            ;
  +        }
  +        
  +	try {
  +	    ostream.flush();
  +	} catch (Throwable t) {
  +	    ;
  +	}
  +	try {
  +	    ostream.close();
  +	} catch (Throwable t) {
  +	    ;
  +	}
  +
  +	// Rethrow any exception that has occurred
  +	if (exception != null)
  +	    throw exception;
  +
  +    }
  +
  +
  +    /**
  +     * Copy the contents of the specified input stream to the specified
  +     * output stream, and ensure that both streams are closed before returning
  +     * (even in the face of an exception).
  +     *
  +     * @param resourceInfo The ResourceInfo object
  +     * @param ostream The output stream to write to
  +     * @param range Range the client wanted to retrieve
  +     * @exception IOException if an input/output error occurs
  +     */
  +    private void copy(ResourceInfo resourceInfo, 
  +                      InputStream resourceInputStream, 
  +                      ServletOutputStream ostream, 
  +                      Range range)
  +	throws IOException {
  +        
  +        IOException exception = null;
  +        
  +        InputStream istream =
  +            new BufferedInputStream(resourceInputStream, input);
  +        exception = copyRange(istream, ostream, range.start, range.end);
  +        
  +	// Clean up the input and output streams
  +	try {
  +	    istream.close();
  +	} catch (Throwable t) {
  +	    ;
  +	}
  +	try {
  +	    ostream.flush();
  +	} catch (Throwable t) {
  +	    ;
  +	}
  +	try {
  +	    ostream.close();
  +	} catch (Throwable t) {
  +	    ;
  +	}
  +
  +	// Rethrow any exception that has occurred
  +	if (exception != null)
  +	    throw exception;
  +        
  +    }
  +
  +
  +    /**
  +     * Copy the contents of the specified input stream to the specified
  +     * output stream, and ensure that both streams are closed before returning
  +     * (even in the face of an exception).
  +     *
  +     * @param resourceInfo The ResourceInfo object
  +     * @param ostream The output stream to write to
  +     * @param ranges Enumeration of the ranges the client wanted to retrieve
  +     * @param contentType Content type of the resource
  +     * @exception IOException if an input/output error occurs
  +     */
  +    private void copy(ResourceInfo resourceInfo, 
  +                      InputStream resourceInputStream, 
  +                      ServletOutputStream ostream,
  +                      Enumeration ranges, String contentType)
  +	throws IOException {
  +        
  +        IOException exception = null;
  +        
  +        while ( (exception == null) && (ranges.hasMoreElements()) ) {
  +            
  +            InputStream istream = 
  +                new BufferedInputStream(resourceInputStream, input);
  +        
  +            Range currentRange = (Range) ranges.nextElement();
  +            
  +            // Writing MIME header.
  +            ostream.println("--" + mimeSeparation);
  +            if (contentType != null)
  +                ostream.println("Content-Type: " + contentType);
  +            ostream.println("Content-Range: bytes " + currentRange.start
  +                           + "-" + currentRange.end + "/" 
  +                           + currentRange.length);
  +            ostream.println();
  +            
  +            // Printing content
  +            exception = copyRange(istream, ostream, currentRange.start,
  +                                  currentRange.end);
  +            
  +            try {
  +                istream.close();
  +            } catch (Throwable t) {
  +                ;
  +            }
  +            
  +        }
  +        
  +        ostream.print("--" + mimeSeparation + "--");
  +        
  +	// Clean up the output streams
  +	try {
  +	    ostream.flush();
  +	} catch (Throwable t) {
  +	    ;
  +	}
  +	try {
  +	    ostream.close();
  +	} catch (Throwable t) {
  +	    ;
  +	}
  +
  +	// Rethrow any exception that has occurred
  +	if (exception != null)
  +	    throw exception;
  +        
  +    }
  +
  +
  +    /**
  +     * Copy the contents of the specified input stream to the specified
  +     * output stream, and ensure that both streams are closed before returning
  +     * (even in the face of an exception).
  +     *
  +     * @param istream The input stream to read from
  +     * @param ostream The output stream to write to
  +     * @return Exception which occured during processing
  +     */
  +    private IOException copyRange(InputStream istream, 
  +                                  ServletOutputStream ostream) {
  +        
  +	// Copy the input stream to the output stream
  +	IOException exception = null;
  +	byte buffer[] = new byte[input];
  +	int len = buffer.length;
  +	while (true) {
  +	    try {
  +                len = istream.read(buffer);
  +                if (len == -1)
  +                    break;
  +                ostream.write(buffer, 0, len);
  +	    } catch (IOException e) {
  +		exception = e;
  +		len = -1;
  +                break;
  +	    }
  +	}
  +        return exception;
  +        
  +    }
  +
  +
  +    /**
  +     * Copy the contents of the specified input stream to the specified
  +     * output stream, and ensure that both streams are closed before returning
  +     * (even in the face of an exception).
  +     *
  +     * @param istream The input stream to read from
  +     * @param ostream The output stream to write to
  +     * @param start Start of the range which will be copied
  +     * @param end End of the range which will be copied
  +     * @return Exception which occured during processing
  +     */
  +    private IOException copyRange(InputStream istream, 
  +                                  ServletOutputStream ostream,
  +                                  long start, long end) {
  +        
  +        try {
  +            istream.skip(start);
  +        } catch (IOException e) {
  +            return e;
  +        }
  +        
  +	IOException exception = null;
  +        long bytesToRead = end - start + 1;
  +        
  +	byte buffer[] = new byte[input];
  +	int len = buffer.length;
  +	while ( (bytesToRead > 0) && (len >= buffer.length)) {
  +	    try {
  +                len = istream.read(buffer);
  +                if (bytesToRead >= len) {
  +                    ostream.write(buffer, 0, len);
  +                    bytesToRead -= len; 
  +                } else {
  +                    ostream.write(buffer, 0, (int) bytesToRead);
  +                    bytesToRead = 0;
  +                }
  +	    } catch (IOException e) {
  +		exception = e;
  +		len = -1;
  +	    }
  +	    if (len < buffer.length)
  +		break;
  +	}
  +        
  +        return exception;
  +        
  +    }
  +
  +
  +    /**
  +     * Parse the range header.
  +     * 
  +     * @param request The servlet request we are processing
  +     * @param response The servlet response we are creating
  +     * @return Vector of ranges
  +     */
  +    private Vector parseRange(HttpServletRequest request, 
  +                              HttpServletResponse response, 
  +                              ResourceInfo resourceInfo) 
  +        throws IOException {
  +        
  +        // Checking If-Range
  +        String headerValue = request.getHeader("If-Range");
  +        if (headerValue != null) {
  +            
  +            String eTag = getETag(resourceInfo, true);
  +            long lastModified = resourceInfo.date;
  +            
  +            Date date = null;
  +            
  +            // Parsing the HTTP Date
  +            for (int i = 0; (date == null) && (i < formats.length); i++) {
  +                try {
  +                    date = formats[i].parse(headerValue);
  +                } catch (ParseException e) {
  +                    ;
  +                }
  +            }
  +            
  +            if (date == null) {
  +                
  +                // If the ETag the client gave does not match the entity
  +                // etag, then the entire entity is returned.
  +                if (!eTag.equals(headerValue.trim()))
  +                    return null;
  +                
  +            } else {
  +                
  +                // If the timestamp of the entity the client got is older than
  +                // the last modification date of the entity, the entire entity
  +                // is returned.
  +                if (lastModified > (date.getTime() + 1000))
  +                    return null;
  +                
  +            }
  +            
  +        }
  +        
  +        long fileLength = resourceInfo.length;
  +        
  +        if (fileLength == 0)
  +            return null;
  +        
  +        // Retrieving the range header (if any is specified
  +        String rangeHeader = request.getHeader("Range");
  +        
  +        if (rangeHeader == null)
  +            return null;
  +        // bytes is the only range unit supported (and I don't see the point
  +        // of adding new ones).
  +        if (!rangeHeader.startsWith("bytes")) {
  +            response.sendError
  +                (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  +            return null;
  +        }
  +        
  +        rangeHeader = rangeHeader.substring(6);
  +        
  +        // Vector which will contain all the ranges which are successfully
  +        // parsed.
  +        Vector result = new Vector();
  +        StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
  +        
  +        // Parsing the range list
  +        while (commaTokenizer.hasMoreTokens()) {
  +            String rangeDefinition = commaTokenizer.nextToken();
  +            
  +            Range currentRange = new Range();
  +            currentRange.length = fileLength;
  +            
  +            int dashPos = rangeDefinition.indexOf('-');
  +            
  +            if (dashPos == -1) {
  +                response.sendError
  +                    (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  +                return null;
  +            }
  +            
  +            if (dashPos == 0) {
  +                
  +                try {
  +                    long offset = Long.parseLong(rangeDefinition);
  +                    currentRange.start = fileLength + offset;
  +                    currentRange.end = fileLength - 1;
  +                } catch (NumberFormatException e) {
  +                    response.sendError
  +                        (HttpServletResponse
  +                         .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  +                    return null;
  +                }
  +                
  +            } else {
  +                
  +                try {
  +                    currentRange.start = Long.parseLong
  +                        (rangeDefinition.substring(0, dashPos));
  +                    if (dashPos < rangeDefinition.length() - 1)
  +                        currentRange.end = Long.parseLong
  +                            (rangeDefinition.substring
  +                             (dashPos + 1, rangeDefinition.length()));
  +                    else
  +                        currentRange.end = fileLength - 1;
  +                } catch (NumberFormatException e) {
  +                    response.sendError
  +                        (HttpServletResponse
  +                         .SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  +                    return null;
  +                }
  +                
  +            }
  +            
  +            if (!currentRange.validate()) {
  +                response.sendError
  +                    (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  +                return null;
  +            }
  +            
  +            result.addElement(currentRange);
  +        }
  +        
  +        return result;
  +    }
  +
  +
  +    // ------------------------------------------------------ Range Inner Class
  +    
  +    
  +    private class Range {
  +        
  +        public long start;
  +        public long end;
  +        public long length;
  +        
  +        /**
  +         * Validate range.
  +         */
  +        public boolean validate() {
  +            return ( (start >= 0) && (end >= 0) && (length > 0)
  +                     && (start <= end) && (end < length) );
  +        }
  +        
  +    }
  +    
  +    
  +    // ----------------------------------------------  ResourceInfo Inner Class
  +    
  +    
  +    private class ResourceInfo {
  +        
  +        
  +        /**
  +         * Constructor.
  +         * 
  +         * @param pathname Path name of the file
  +         */
  +        public ResourceInfo(String path, NodeRevisionDescriptor properties) {
  +            
  +            this.path = path;
  +            this.exists = true;
  +            this.creationDate = properties.getCreationDateAsDate().getTime();
  +            this.date = properties.getLastModifiedAsDate().getTime();
  +            this.httpDate = properties.getLastModified();
  +            this.length = properties.getContentLength();
  +            
  +        }
  +        
  +        
  +        public String path;
  +        public long creationDate;
  +        public String httpDate;
  +        public long date;
  +        public long length;
  +        //public boolean collection;
  +        public boolean exists;
  +        
  +        
  +        /**
  +         * Test if the associated resource exists.
  +         */
  +        public boolean exists() {
  +            return exists;
  +        }
  +        
  +        
  +        /**
  +         * String representation.
  +         */
  +        public String toString() {
  +            return path;
  +        }
  +        
  +        
  +    }
  +    
       
   }
  
  
  
  1.8       +6 -6      jakarta-slide/src/share/org/apache/slide/webdav/method/PutMethod.java
  
  Index: PutMethod.java
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/share/org/apache/slide/webdav/method/PutMethod.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- PutMethod.java	2000/09/21 22:46:32	1.7
  +++ PutMethod.java	2000/09/25 07:24:50	1.8
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/webdav/method/PutMethod.java,v 1.7 2000/09/21 22:46:32 remm Exp $
  - * $Revision: 1.7 $
  - * $Date: 2000/09/21 22:46:32 $
  + * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/webdav/method/PutMethod.java,v 1.8 2000/09/25 07:24:50 remm Exp $
  + * $Revision: 1.8 $
  + * $Date: 2000/09/25 07:24:50 $
    *
    * ====================================================================
    *
  @@ -168,7 +168,7 @@
                   // Last modification date
                   DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
                   property = new NodeProperty("getlastmodified", 
  -                                            formatter.format(new Date()), 
  +                                            new Date(), 
                                               true);
                   revisionDescriptor.setProperty(property);
                   
  @@ -204,7 +204,7 @@
                   // Creation date
                   DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
                   property = new NodeProperty("creationdate", 
  -                                            formatter.format(new Date()), 
  +                                            new Date(), 
                                               true);
                   revisionDescriptor.setProperty(property);
                   
  @@ -242,7 +242,7 @@
                   
                   // Last modification date
                   property = new NodeProperty
  -                    ("getlastmodified", formatter.format(new Date()), true);
  +                    ("getlastmodified", new Date(), true);
                   revisionDescriptor.setProperty(property);
                   
                   // Etag generation
  
  
  
  1.8       +23 -3     jakarta-slide/src/share/org/apache/slide/webdav/method/WebdavMethod.java
  
  Index: WebdavMethod.java
  ===================================================================
  RCS file: /home/cvs/jakarta-slide/src/share/org/apache/slide/webdav/method/WebdavMethod.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- WebdavMethod.java	2000/09/20 06:06:18	1.7
  +++ WebdavMethod.java	2000/09/25 07:24:51	1.8
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/webdav/method/WebdavMethod.java,v 1.7 2000/09/20 06:06:18 remm Exp $
  - * $Revision: 1.7 $
  - * $Date: 2000/09/20 06:06:18 $
  + * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/webdav/method/WebdavMethod.java,v 1.8 2000/09/25 07:24:51 remm Exp $
  + * $Revision: 1.8 $
  + * $Date: 2000/09/25 07:24:51 $
    *
    * ====================================================================
    *
  @@ -436,6 +436,26 @@
       }
       
       
  +    /**
  +     * Tests if a resource is a collection.
  +     */
  +    protected boolean isCollection(NodeRevisionDescriptor revisionDescriptor) {
  +        
  +        boolean result = false;
  +        
  +        if (revisionDescriptor == null)
  +            return true;
  +        
  +        NodeProperty property = revisionDescriptor.getProperty("resourcetype");
  +        
  +        if ((property != null) 
  +            && (property.getValue().equals("<collection/>"))) {
  +            result = true;
  +        }
  +        
  +        return result;
  +        
  +    }
       /**
        * Parse WebDAV XML query.
        *