1 package com.bradmcevoy.http.webdav;
2
3 import com.bradmcevoy.http.*;
4 import com.bradmcevoy.http.AuthenticationService.AuthStatus;
5 import com.bradmcevoy.http.exceptions.BadRequestException;
6 import com.bradmcevoy.http.exceptions.ConflictException;
7 import com.bradmcevoy.http.exceptions.NotAuthorizedException;
8 import com.bradmcevoy.http.webdav.PropPatchRequestParser.ParseResult;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.util.ArrayList;
12 import java.util.Iterator;
13 import java.util.List;
14
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17
18 import com.bradmcevoy.http.Request.Method;
19 import com.bradmcevoy.http.Response.Status;
20 import com.bradmcevoy.io.ReadingException;
21 import com.bradmcevoy.io.WritingException;
22 import com.bradmcevoy.property.DefaultPropertyAuthoriser;
23 import com.bradmcevoy.property.PropertyHandler;
24 import com.bradmcevoy.property.PropertyAuthoriser;
25 import com.ettrema.event.PropPatchEvent;
26 import java.util.HashSet;
27 import java.util.Set;
28 import javax.xml.namespace.QName;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 public class PropPatchHandler implements ExistingEntityHandler, PropertyHandler {
102
103 private final static Logger log = LoggerFactory.getLogger( PropPatchHandler.class );
104 private final ResourceHandlerHelper resourceHandlerHelper;
105 private final PropPatchRequestParser requestParser;
106 private final PropPatchSetter patchSetter;
107 private final WebDavResponseHandler responseHandler;
108 private PropertyAuthoriser permissionService = new DefaultPropertyAuthoriser();
109
110 public PropPatchHandler( ResourceHandlerHelper resourceHandlerHelper, WebDavResponseHandler responseHandler, PropPatchSetter propPatchSetter ) {
111 this.resourceHandlerHelper = resourceHandlerHelper;
112 this.requestParser = new DefaultPropPatchParser();
113 patchSetter = propPatchSetter;
114 this.responseHandler = responseHandler;
115 }
116
117 public PropPatchHandler( ResourceHandlerHelper resourceHandlerHelper, PropPatchRequestParser requestParser, PropPatchSetter patchSetter, WebDavResponseHandler responseHandler ) {
118 this.resourceHandlerHelper = resourceHandlerHelper;
119 this.requestParser = requestParser;
120 this.patchSetter = patchSetter;
121 this.responseHandler = responseHandler;
122 }
123
124 public String[] getMethods() {
125 return new String[]{Method.PROPPATCH.code};
126 }
127
128 public boolean isCompatible( Resource r ) {
129 return patchSetter.supports( r );
130 }
131
132 public void process( HttpManager httpManager, Request request, Response response ) throws ConflictException, NotAuthorizedException, BadRequestException {
133 resourceHandlerHelper.process( httpManager, request, response, this );
134 }
135
136 public void processResource( HttpManager manager, Request request, Response response, Resource resource ) throws NotAuthorizedException, ConflictException, BadRequestException {
137 long t = System.currentTimeMillis();
138 try {
139
140 manager.onProcessResourceStart( request, response, resource );
141
142 if( resourceHandlerHelper.isNotCompatible( resource, request.getMethod() ) || !isCompatible( resource ) ) {
143 log.debug( "resource not compatible. Resource class: " + resource.getClass() + " handler: " + getClass() );
144 responseHandler.respondMethodNotImplemented( resource, response, request );
145 return;
146 }
147
148 AuthStatus authStatus = resourceHandlerHelper.checkAuthentication( manager, resource, request );
149 if( authStatus != null && authStatus.loginFailed ) {
150 log.debug( "authentication failed. respond with: " + responseHandler.getClass().getCanonicalName() + " resource: " + resource.getClass().getCanonicalName() );
151 responseHandler.respondUnauthorised( resource, response, request );
152 return;
153 }
154
155 if( request.getMethod().isWrite ) {
156 if( resourceHandlerHelper.isLockedOut( request, resource ) ) {
157 response.setStatus( Status.SC_LOCKED );
158 return;
159 }
160 }
161
162 processExistingResource( manager, request, response, resource );
163 } finally {
164 t = System.currentTimeMillis() - t;
165 manager.onProcessResourceFinish( request, response, resource, t );
166 }
167 }
168
169 public void processExistingResource( HttpManager manager, Request request, Response response, Resource resource ) throws NotAuthorizedException, BadRequestException, ConflictException {
170
171 try {
172 PropFindResponse resp = doPropPatch( request, resource);
173
174 manager.getEventManager().fireEvent( new PropPatchEvent( resource, resp ) );
175 List<PropFindResponse> responses = new ArrayList<PropFindResponse>();
176 responses.add( resp );
177 responseHandler.respondPropFind( responses, response, request, resource );
178 } catch( NotAuthorizedException e ) {
179 responseHandler.respondUnauthorised( resource, response, request );
180 } catch( WritingException ex ) {
181 throw new RuntimeException( ex );
182 } catch( ReadingException ex ) {
183 throw new RuntimeException( ex );
184 } catch( IOException ex ) {
185 throw new RuntimeException( ex );
186 }
187 }
188
189 public PropFindResponse doPropPatch(Request request, Resource resource) throws NotAuthorizedException, IOException {
190 InputStream in = request.getInputStream();
191 ParseResult parseResult = requestParser.getRequestedFields(in);
192
193 Set<QName> allFields = getAllFields(parseResult);
194 if (log.isTraceEnabled()) {
195 log.trace("check permissions with: " + permissionService.getClass().getCanonicalName());
196 }
197 Set<PropertyAuthoriser.CheckResult> errorFields = permissionService.checkPermissions(request, request.getMethod(), PropertyAuthoriser.PropertyPermission.WRITE, allFields, resource);
198 if (errorFields != null && errorFields.size() > 0) {
199 throw new NotAuthorizedException(resource);
200 }
201 String href = request.getAbsoluteUrl();
202 PropFindResponse resp = patchSetter.setProperties(href, parseResult, resource);
203 return resp;
204 }
205
206 private Set<QName> getAllFields( ParseResult parseResult ) {
207 Set<QName> set = new HashSet<QName>();
208 if( parseResult.getFieldsToRemove() != null ) {
209 set.addAll( parseResult.getFieldsToRemove() );
210 }
211 if( parseResult.getFieldsToSet() != null ) {
212 set.addAll( parseResult.getFieldsToSet().keySet() );
213 }
214 return set;
215 }
216
217 public PropertyAuthoriser getPermissionService() {
218 return permissionService;
219 }
220
221 public void setPermissionService( PropertyAuthoriser permissionService ) {
222 this.permissionService = permissionService;
223 }
224
225 public static class Field {
226
227 public final String name;
228 String namespaceUri;
229
230 public Field( String name ) {
231 this.name = name;
232 }
233
234 public void setNamespaceUri( String namespaceUri ) {
235 this.namespaceUri = namespaceUri;
236 }
237
238 public String getNamespaceUri() {
239 return namespaceUri;
240 }
241 }
242
243 public static class SetField extends Field {
244
245 public final String value;
246
247 public SetField( String name, String value ) {
248 super( name );
249 this.value = value;
250 }
251 }
252
253 public static class Fields implements Iterable<Field> {
254
255
256
257
258 public final List<Field> removeFields = new ArrayList<Field>();
259
260
261
262 public final List<SetField> setFields = new ArrayList<PropPatchHandler.SetField>();
263
264 private int size() {
265 return removeFields.size() + setFields.size();
266 }
267
268 public Iterator<Field> iterator() {
269 List<Field> list = new ArrayList<Field>( removeFields );
270 list.addAll( setFields );
271 return list.iterator();
272 }
273 }
274 }