1 package com.bradmcevoy.http.webdav;
2
3 import com.bradmcevoy.http.*;
4 import com.bradmcevoy.http.Request.Method;
5 import com.bradmcevoy.http.exceptions.BadRequestException;
6 import com.bradmcevoy.http.exceptions.ConflictException;
7 import com.bradmcevoy.http.exceptions.NotAuthorizedException;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10
11 import java.net.URI;
12
13 public class CopyHandler implements ExistingEntityHandler {
14
15 private Logger log = LoggerFactory.getLogger(CopyHandler.class);
16 private final WebDavResponseHandler responseHandler;
17 private final HandlerHelper handlerHelper;
18 private final ResourceHandlerHelper resourceHandlerHelper;
19 private DeleteHelper deleteHelper;
20 private UserAgentHelper userAgentHelper = new DefaultUserAgentHelper();
21 private boolean deleteExistingBeforeCopy = true;
22
23 public CopyHandler(WebDavResponseHandler responseHandler, HandlerHelper handlerHelper, ResourceHandlerHelper resourceHandlerHelper) {
24 this.responseHandler = responseHandler;
25 this.handlerHelper = handlerHelper;
26 this.resourceHandlerHelper = resourceHandlerHelper;
27 this.deleteHelper = new DeleteHelperImpl(handlerHelper);
28 }
29
30 public String[] getMethods() {
31 return new String[]{Method.COPY.code};
32 }
33
34 @Override
35 public boolean isCompatible(Resource handler) {
36 return (handler instanceof CopyableResource);
37 }
38
39 public void processResource(HttpManager manager, Request request, Response response, Resource r) throws NotAuthorizedException, ConflictException, BadRequestException {
40 resourceHandlerHelper.processResource(manager, request, response, r, this);
41 }
42
43 public void process(HttpManager httpManager, Request request, Response response) throws ConflictException, NotAuthorizedException, BadRequestException {
44 resourceHandlerHelper.process(httpManager, request, response, this);
45 }
46
47 public void processExistingResource(HttpManager manager, Request request, Response response, Resource resource) throws NotAuthorizedException, BadRequestException, ConflictException {
48 CopyableResource r = (CopyableResource) resource;
49 String sDest = request.getDestinationHeader();
50 sDest = HttpManager.decodeUrl(sDest);
51 URI destUri = URI.create(sDest);
52 sDest = destUri.getPath();
53
54 Dest dest = new Dest(destUri.getHost(), sDest);
55 Resource rDest = manager.getResourceFactory().getResource(dest.host, dest.url);
56 log.debug("process: copying from: " + r.getName() + " -> " + dest.url + "/" + dest.name);
57
58 if (rDest == null) {
59 log.debug("process: destination parent does not exist: " + sDest);
60 responseHandler.respondConflict(resource, response, request, "Destination does not exist: " + sDest);
61 } else if (!(rDest instanceof CollectionResource)) {
62 log.debug("process: destination exists but is not a collection");
63 responseHandler.respondConflict(resource, response, request, "Destination exists but is not a collection: " + sDest);
64 } else {
65 log.debug("process: copy resource to: " + rDest.getName());
66
67 Resource fDest = manager.getResourceFactory().getResource(dest.host, dest.url + "/" + dest.name);
68 if (handlerHelper.isLockedOut(request, fDest)) {
69 responseHandler.respondLocked(request, response, resource);
70 return;
71 } else {
72 boolean wasDeleted = false;
73 CollectionResource colDest = (CollectionResource) rDest;
74 Resource rExisting = colDest.child(dest.name);
75 if (rExisting != null) {
76 if( !canOverwrite( request ) ) {
77
78 responseHandler.respondPreconditionFailed(request, response, resource);
79 return;
80 } else {
81
82 if (deleteHelper.isLockedOut(request, rExisting)) {
83 log.info("copy destination exists but is locked");
84 responseHandler.respondPreconditionFailed(request, response, resource);
85 return;
86 } else {
87 if (deleteExistingBeforeCopy) {
88 if (rExisting instanceof DeletableResource) {
89 log.debug("copy destination exists and is deletable, delete it..");
90 DeletableResource dr = (DeletableResource) rExisting;
91 deleteHelper.delete(dr);
92 wasDeleted = true;
93 } else {
94 log.warn("copy destination exists and is a collection so must be deleted, but does not implement: " + DeletableResource.class);
95 responseHandler.respondConflict(rExisting, response, request, sDest);
96 return;
97 }
98 }
99 }
100 }
101 }
102 r.copyTo(colDest, dest.name);
103
104
105 if (wasDeleted) {
106 responseHandler.respondNoContent( resource, response, request );
107 } else {
108 responseHandler.respondCreated(resource, response, request);
109 }
110
111 }
112 }
113 }
114
115 public void setDeleteExistingBeforeCopy(boolean deleteExistingBeforeCopy) {
116 this.deleteExistingBeforeCopy = deleteExistingBeforeCopy;
117 }
118
119 public boolean isDeleteExistingBeforeCopy() {
120 return deleteExistingBeforeCopy;
121 }
122
123 private boolean canOverwrite( Request request ) {
124 Boolean ow = request.getOverwriteHeader();
125 boolean bHasOverwriteHeader = ( ow != null && request.getOverwriteHeader().booleanValue() );
126 if( bHasOverwriteHeader) {
127 return true;
128 } else {
129 String us = request.getUserAgentHeader();
130 if( userAgentHelper.isMacFinder( us)) {
131 log.debug( "no overwrite header, but user agent is Finder so permit overwrite");
132 return true;
133 } else {
134 return false;
135 }
136 }
137 }
138
139 public UserAgentHelper getUserAgentHelper() {
140 return userAgentHelper;
141 }
142
143 public void setUserAgentHelper( UserAgentHelper userAgentHelper ) {
144 this.userAgentHelper = userAgentHelper;
145 }
146
147 }