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