1 package com.bradmcevoy.http.webdav;
2
3 import com.bradmcevoy.http.*;
4 import com.bradmcevoy.http.exceptions.BadRequestException;
5 import com.bradmcevoy.http.exceptions.ConflictException;
6 import com.bradmcevoy.http.exceptions.LockedException;
7 import com.bradmcevoy.http.exceptions.NotAuthorizedException;
8 import com.bradmcevoy.http.exceptions.PreConditionFailedException;
9 import org.apache.commons.io.output.ByteArrayOutputStream;
10 import java.io.IOException;
11
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14 import org.xml.sax.SAXException;
15
16 import com.bradmcevoy.common.Path;
17 import com.bradmcevoy.http.Request.Method;
18 import com.bradmcevoy.http.Response.Status;
19
20
21
22
23
24
25 public class LockHandler implements ResourceHandler {
26
27 private static final Logger log = LoggerFactory.getLogger( LockHandler.class );
28 private final WebDavResponseHandler responseHandler;
29 private final HandlerHelper handlerHelper;
30
31 private LockWriterHelper lockWriterHelper;
32
33 public LockHandler( WebDavResponseHandler responseHandler, HandlerHelper handlerHelper ) {
34 this.responseHandler = responseHandler;
35 this.handlerHelper = handlerHelper;
36 lockWriterHelper = new LockWriterHelper();
37 }
38
39 public LockWriterHelper getLockWriterHelper() {
40 return lockWriterHelper;
41 }
42
43 public void setLockWriterHelper( LockWriterHelper lockWriterHelper ) {
44 this.lockWriterHelper = lockWriterHelper;
45 }
46
47
48 public void processResource( HttpManager manager, Request request, Response response, Resource r ) throws NotAuthorizedException, ConflictException, BadRequestException {
49 throw new UnsupportedOperationException( "Not supported yet." );
50 }
51
52 public String[] getMethods() {
53 return new String[]{Method.LOCK.code};
54 }
55
56 @Override
57 public void process( HttpManager manager, Request request, Response response ) throws NotAuthorizedException {
58 if( !handlerHelper.checkExpects( responseHandler, request, response ) ) {
59 return;
60 }
61
62 String host = request.getHostHeader();
63 String url = HttpManager.decodeUrl( request.getAbsolutePath() );
64
65
66 Resource r = manager.getResourceFactory().getResource( host, url );
67 if( r != null ) {
68 log.debug( "locking existing resource: " + r.getName() );
69 processExistingResource( manager, request, response, r );
70 } else {
71 log.debug( "lock target doesnt exist, attempting lock null.." );
72 processNonExistingResource( manager, request, response, host, url );
73 }
74 }
75
76 protected void processExistingResource( HttpManager manager, Request request, Response response, Resource resource ) throws NotAuthorizedException {
77 if( handlerHelper.isNotCompatible( resource, request.getMethod()) || !isCompatible( resource ) ) {
78 responseHandler.respondMethodNotImplemented( resource, response, request );
79 return;
80 }
81 if( !handlerHelper.checkAuthorisation( manager, resource, request ) ) {
82 responseHandler.respondUnauthorised( resource, response, request );
83 return;
84 }
85
86 handlerHelper.checkExpects( responseHandler, request, response );
87
88 LockableResource r = (LockableResource) resource;
89 LockTimeout timeout = LockTimeout.parseTimeout( request );
90 String ifHeader = request.getIfHeader();
91 response.setContentTypeHeader( Response.XML );
92 if( ifHeader == null || ifHeader.length() == 0 ) {
93 processNewLock( manager, request, response, r, timeout );
94 } else {
95 processRefresh( manager, request, response, r, timeout, ifHeader );
96 }
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 private void processNonExistingResource( HttpManager manager, Request request, Response response, String host, String url ) throws NotAuthorizedException {
129 String name;
130
131 Path parentPath = Path.path( url );
132 name = parentPath.getName();
133 parentPath = parentPath.getParent();
134 url = parentPath.toString();
135
136 Resource r = manager.getResourceFactory().getResource( host, url );
137 if( r != null ) {
138 if( !handlerHelper.checkAuthorisation( manager, r, request ) ) {
139 responseHandler.respondUnauthorised( r, response, request );
140 return;
141 } else {
142 processCreateAndLock( manager, request, response, r, name );
143 }
144 } else {
145 log.debug( "couldnt find parent to execute lock-null, returning not found" );
146
147 response.setStatus( Status.SC_CONFLICT );
148
149 }
150 }
151
152 private void processCreateAndLock( HttpManager manager, Request request, Response response, Resource parentResource, String name ) throws NotAuthorizedException {
153 if( parentResource instanceof LockingCollectionResource ) {
154 log.debug( "parent supports lock-null. doing createAndLock" );
155 LockingCollectionResource lockingParent = (LockingCollectionResource) parentResource;
156 LockTimeout timeout = LockTimeout.parseTimeout( request );
157 response.setContentTypeHeader( Response.XML );
158
159 LockInfo lockInfo;
160 try {
161 lockInfo = LockInfo.parseLockInfo( request );
162 } catch( SAXException ex ) {
163 throw new RuntimeException( "Exception reading request body", ex );
164 } catch( IOException ex ) {
165 throw new RuntimeException( "Exception reading request body", ex );
166 }
167
168
169
170 log.debug( "Creating lock on unmapped resource: " + name );
171 LockToken tok = lockingParent.createAndLock( name, timeout, lockInfo );
172 if( tok == null ) {
173 throw new RuntimeException( "createAndLock returned null, from resource of type: " + lockingParent.getClass().getCanonicalName());
174 }
175 response.setStatus( Status.SC_CREATED );
176 response.setLockTokenHeader( "<opaquelocktoken:" + tok.tokenId + ">" );
177 respondWithToken( tok, request, response );
178
179 } else {
180 log.debug( "parent does not support lock-null, respondong method not allowed" );
181 responseHandler.respondMethodNotImplemented( parentResource, response, request );
182 }
183 }
184
185 @Override
186 public boolean isCompatible( Resource handler ) {
187 return handler instanceof LockableResource;
188 }
189
190 protected void processNewLock( HttpManager milton, Request request, Response response, LockableResource r, LockTimeout timeout ) throws NotAuthorizedException {
191 LockInfo lockInfo;
192 try {
193 lockInfo = LockInfo.parseLockInfo( request );
194 } catch( SAXException ex ) {
195 throw new RuntimeException( "Exception reading request body", ex );
196 } catch( IOException ex ) {
197 throw new RuntimeException( "Exception reading request body", ex );
198 }
199
200 if( handlerHelper.isLockedOut( request, r ) ) {
201 this.responseHandler.respondLocked( request, response, r );
202 return;
203 }
204
205 log.debug( "locking: " + r.getName() );
206 LockResult result;
207 try {
208 result = r.lock( timeout, lockInfo );
209 } catch( PreConditionFailedException ex ) {
210 responseHandler.respondPreconditionFailed( request, response, r );
211 return ;
212 } catch( LockedException ex ) {
213 responseHandler.respondLocked( request, response, r );
214 return ;
215 }
216
217 if( result.isSuccessful() ) {
218 LockToken tok = result.getLockToken();
219 log.debug( "..locked ok: " + tok.tokenId );
220 response.setLockTokenHeader( "<opaquelocktoken:" + tok.tokenId + ">" );
221 respondWithToken( tok, request, response );
222 } else {
223 respondWithLockFailure( result, request, response );
224 }
225 }
226
227 protected void processRefresh( HttpManager milton, Request request, Response response, LockableResource r, LockTimeout timeout, String ifHeader ) throws NotAuthorizedException {
228 String token = parseToken( ifHeader );
229 log.debug( "refreshing lock: " + token );
230 LockResult result;
231 try {
232 result = r.refreshLock( token );
233 } catch( PreConditionFailedException ex ) {
234 responseHandler.respondPreconditionFailed( request, response, r );
235 return ;
236 }
237 if( result.isSuccessful() ) {
238 LockToken tok = result.getLockToken();
239 respondWithToken( tok, request, response );
240 } else {
241 respondWithLockFailure( result, request, response );
242 }
243 }
244
245 protected void respondWithToken( LockToken tok, Request request, Response response ) {
246 response.setStatus( Status.SC_OK );
247 ByteArrayOutputStream out = new ByteArrayOutputStream();
248 XmlWriter writer = new XmlWriter( out );
249 writer.writeXMLHeader();
250 writer.open( "D:prop xmlns:D=\"DAV:\"" );
251 writer.newLine();
252 writer.open( "D:lockdiscovery" );
253 writer.newLine();
254 writer.open( "D:activelock" );
255 writer.newLine();
256 lockWriterHelper.appendType( writer, tok.info.type );
257 lockWriterHelper.appendScope( writer, tok.info.scope );
258 lockWriterHelper.appendDepth( writer, tok.info.depth );
259 lockWriterHelper.appendOwner( writer, tok.info.lockedByUser );
260 lockWriterHelper.appendTimeout( writer, tok.timeout.getSeconds() );
261 lockWriterHelper.appendTokenId( writer, tok.tokenId );
262 lockWriterHelper.appendRoot( writer, request.getAbsoluteUrl() );
263 writer.close( "D:activelock" );
264 writer.close( "D:lockdiscovery" );
265 writer.close( "D:prop" );
266 writer.flush();
267
268 log.debug( "lock response: " + out.toString() );
269 try {
270 response.getOutputStream().write( out.toByteArray() );
271 } catch( IOException ex ) {
272 log.warn( "exception writing to outputstream", ex );
273 }
274
275
276 }
277
278 static String parseToken( String ifHeader ) {
279 String token = ifHeader;
280 int pos = token.indexOf( ":" );
281 if( pos >= 0 ) {
282 token = token.substring( pos + 1 );
283 pos = token.indexOf( ">" );
284 if( pos >= 0 ) {
285 token = token.substring( 0, pos );
286 }
287 }
288 return token;
289 }
290
291
292 private void respondWithLockFailure( LockResult result, Request request, Response response ) {
293 log.info("respondWithLockFailure: " + result.getFailureReason().name());
294 response.setStatus( result.getFailureReason().status );
295
296 }
297 }