1 package org.apache.maven.wagon.providers.webdav;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.http.HttpException;
23 import org.apache.http.HttpStatus;
24 import org.apache.http.client.methods.CloseableHttpResponse;
25 import org.apache.http.util.EntityUtils;
26 import org.apache.jackrabbit.webdav.DavConstants;
27 import org.apache.jackrabbit.webdav.DavException;
28 import org.apache.jackrabbit.webdav.MultiStatus;
29 import org.apache.jackrabbit.webdav.MultiStatusResponse;
30 import org.apache.jackrabbit.webdav.client.methods.HttpMkcol;
31 import org.apache.jackrabbit.webdav.client.methods.HttpPropfind;
32 import org.apache.jackrabbit.webdav.property.DavProperty;
33 import org.apache.jackrabbit.webdav.property.DavPropertyName;
34 import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
35 import org.apache.jackrabbit.webdav.property.DavPropertySet;
36 import org.apache.maven.wagon.PathUtils;
37 import org.apache.maven.wagon.ResourceDoesNotExistException;
38 import org.apache.maven.wagon.TransferFailedException;
39 import org.apache.maven.wagon.WagonConstants;
40 import org.apache.maven.wagon.authorization.AuthorizationException;
41 import org.apache.maven.wagon.repository.Repository;
42 import org.apache.maven.wagon.shared.http.AbstractHttpClientWagon;
43 import org.codehaus.plexus.util.FileUtils;
44 import org.codehaus.plexus.util.StringUtils;
45 import org.w3c.dom.Node;
46
47 import java.io.File;
48 import java.io.IOException;
49 import java.net.URLDecoder;
50 import java.util.ArrayList;
51 import java.util.List;
52
53 import static org.apache.maven.wagon.shared.http.HttpMessageUtils.formatResourceDoesNotExistMessage;
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class WebDavWagon
69 extends AbstractHttpClientWagon
70 {
71 protected static final String CONTINUE_ON_FAILURE_PROPERTY = "wagon.webdav.continueOnFailure";
72
73 private final boolean continueOnFailure = Boolean.getBoolean( CONTINUE_ON_FAILURE_PROPERTY );
74
75
76
77
78
79
80
81
82
83 private static final String[][] PROTOCOL_MAP =
84 new String[][]{ { "dav:http://", "http://" },
85 { "dav:https://", "https://" },
86 { "dav+http://", "http://" },
87 { "dav+https://", "https://" },
88 { "dav://", "http://" },
89 { "davs://", "https://" } };
90
91
92
93
94
95
96 public boolean supportsDirectoryCopy()
97 {
98 return true;
99 }
100
101
102
103
104
105
106
107
108
109 protected void mkdirs( String dir )
110 throws IOException
111 {
112 Repository repository = getRepository();
113 String basedir = repository.getBasedir();
114
115 String baseUrl = repository.getProtocol() + "://" + repository.getHost();
116 if ( repository.getPort() != WagonConstants.UNKNOWN_PORT )
117 {
118 baseUrl += ":" + repository.getPort();
119 }
120
121
122 String relpath = FileUtils.normalize( getPath( basedir, dir ) + "/" );
123
124 PathNavigator navigator = new PathNavigator( relpath );
125
126
127
128 int status = -1;
129 do
130 {
131 String url = baseUrl + "/" + navigator.getPath();
132 status = doMkCol( url );
133 if ( status == HttpStatus.SC_CREATED || status == HttpStatus.SC_METHOD_NOT_ALLOWED )
134 {
135 break;
136 }
137 }
138 while ( navigator.backward() );
139
140
141 while ( navigator.forward() )
142 {
143 String url = baseUrl + "/" + navigator.getPath();
144 status = doMkCol( url );
145 if ( status != HttpStatus.SC_CREATED )
146 {
147 throw new IOException( "Unable to create collection: " + url + "; status code = " + status );
148 }
149 }
150 }
151
152 private int doMkCol( String url )
153 throws IOException
154 {
155 HttpMkcol method = new HttpMkcol( url );
156 try ( CloseableHttpResponse closeableHttpResponse = execute( method ) )
157 {
158 return closeableHttpResponse.getStatusLine().getStatusCode();
159 }
160 catch ( HttpException e )
161 {
162 throw new IOException( e.getMessage(), e );
163 }
164 finally
165 {
166 if ( method != null )
167 {
168 method.releaseConnection();
169 }
170 }
171 }
172
173
174
175
176
177
178
179
180
181
182 public void putDirectory( File sourceDirectory, String destinationDirectory )
183 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
184 {
185 for ( File file : sourceDirectory.listFiles() )
186 {
187 if ( file.isDirectory() )
188 {
189 putDirectory( file, destinationDirectory + "/" + file.getName() );
190 }
191 else
192 {
193 String target = destinationDirectory + "/" + file.getName();
194
195 put( file, target );
196 }
197 }
198 }
199 private boolean isDirectory( String url )
200 throws IOException, DavException
201 {
202 DavPropertyNameSet nameSet = new DavPropertyNameSet();
203 nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_RESOURCETYPE ) );
204
205 CloseableHttpResponse closeableHttpResponse = null;
206 HttpPropfind method = null;
207 try
208 {
209 method = new HttpPropfind( url, nameSet, DavConstants.DEPTH_0 );
210 closeableHttpResponse = execute( method );
211
212 if ( method.succeeded( closeableHttpResponse ) )
213 {
214 MultiStatus multiStatus = method.getResponseBodyAsMultiStatus( closeableHttpResponse );
215 MultiStatusResponse response = multiStatus.getResponses()[0];
216 DavPropertySet propertySet = response.getProperties( HttpStatus.SC_OK );
217 DavProperty<?> property = propertySet.get( DavConstants.PROPERTY_RESOURCETYPE );
218 if ( property != null )
219 {
220 Node node = (Node) property.getValue();
221 return node.getLocalName().equals( DavConstants.XML_COLLECTION );
222 }
223 }
224 return false;
225 }
226 catch ( HttpException e )
227 {
228 throw new IOException( e.getMessage(), e );
229 }
230 finally
231 {
232
233 if ( method != null )
234 {
235 method.releaseConnection();
236 }
237 if ( closeableHttpResponse != null )
238 {
239 closeableHttpResponse.close();
240 }
241 }
242 }
243
244 public List<String> getFileList( String destinationDirectory )
245 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
246 {
247 String repositoryUrl = repository.getUrl();
248 String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + destinationDirectory;
249
250 HttpPropfind method = null;
251 CloseableHttpResponse closeableHttpResponse = null;
252 try
253 {
254 if ( isDirectory( url ) )
255 {
256 DavPropertyNameSet nameSet = new DavPropertyNameSet();
257 nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_DISPLAYNAME ) );
258
259 method = new HttpPropfind( url, nameSet, DavConstants.DEPTH_1 );
260 closeableHttpResponse = execute( method );
261 if ( method.succeeded( closeableHttpResponse ) )
262 {
263 ArrayList<String> dirs = new ArrayList<>();
264 MultiStatus multiStatus = method.getResponseBodyAsMultiStatus( closeableHttpResponse );
265 for ( int i = 0; i < multiStatus.getResponses().length; i++ )
266 {
267 MultiStatusResponse response = multiStatus.getResponses()[i];
268 String entryUrl = response.getHref();
269 String fileName = PathUtils.filename( URLDecoder.decode( entryUrl ) );
270 if ( entryUrl.endsWith( "/" ) )
271 {
272 if ( i == 0 )
273 {
274
275
276
277 continue;
278 }
279
280
281 fileName = PathUtils.filename( PathUtils.dirname( URLDecoder.decode( entryUrl ) ) ) + "/";
282 }
283
284 if ( !StringUtils.isEmpty( fileName ) )
285 {
286 dirs.add( fileName );
287 }
288 }
289 return dirs;
290 }
291
292 int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();
293 String reasonPhrase = closeableHttpResponse.getStatusLine().getReasonPhrase();
294 if ( statusCode == HttpStatus.SC_NOT_FOUND || statusCode == HttpStatus.SC_GONE )
295 {
296 EntityUtils.consumeQuietly( closeableHttpResponse.getEntity() );
297 throw new ResourceDoesNotExistException( formatResourceDoesNotExistMessage( url, statusCode,
298 reasonPhrase, getProxyInfo() ) );
299 }
300 }
301 }
302 catch ( HttpException e )
303 {
304 throw new TransferFailedException( e.getMessage(), e );
305 }
306 catch ( DavException e )
307 {
308 throw new TransferFailedException( e.getMessage(), e );
309 }
310 catch ( IOException e )
311 {
312 throw new TransferFailedException( e.getMessage(), e );
313 }
314 finally
315 {
316
317 if ( method != null )
318 {
319 method.releaseConnection();
320 }
321 if ( closeableHttpResponse != null )
322 {
323 try
324 {
325 closeableHttpResponse.close();
326 }
327 catch ( IOException e )
328 {
329
330 }
331 }
332 }
333
334 throw new ResourceDoesNotExistException(
335 "Destination path exists but is not a " + "WebDAV collection (directory): " + url );
336 }
337
338 public String getURL( Repository repository )
339 {
340 String url = repository.getUrl();
341
342
343 for ( String[] entry : PROTOCOL_MAP )
344 {
345 String protocol = entry[0];
346 if ( url.startsWith( protocol ) )
347 {
348 return entry[1] + url.substring( protocol.length() );
349 }
350 }
351
352
353 return url;
354 }
355
356
357 public void put( File source, String resourceName )
358 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
359 {
360 try
361 {
362 super.put( source, resourceName );
363 }
364 catch ( TransferFailedException e )
365 {
366 if ( continueOnFailure )
367 {
368
369 System.out.println(
370 "WARN: Skip unable to transfer '" + resourceName + "' from '" + source.getPath() + "' due to "
371 + e.getMessage() );
372 }
373 else
374 {
375 throw e;
376 }
377 }
378 }
379 }