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.client.methods.HttpUriRequest;
28 import org.apache.http.impl.auth.BasicScheme;
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.io.InputStream;
53 import java.net.URLDecoder;
54 import java.util.ArrayList;
55 import java.util.List;
56 import java.util.Properties;
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 private static final String DEFAULT_USER_AGENT = getDefaultUserAgent();
105
106 private static String getDefaultUserAgent()
107 {
108 Properties props = new Properties();
109
110 try ( InputStream is = AbstractHttpClientWagon.class.getResourceAsStream(
111 "/META-INF/maven/org.apache.maven.wagon/wagon-webdav-jackrabbit/pom.properties" ) )
112 {
113 props.load( is );
114 is.close();
115 }
116 catch ( Exception ignore )
117 {
118
119 }
120
121 String ver = props.getProperty( "version", "unknown-version" );
122 return "Apache-Maven-Wagon/" + ver + " (Java " + System.getProperty( "java.version" ) + "; ";
123 }
124
125 @Override
126 protected String getUserAgent( HttpUriRequest method )
127 {
128 String userAgent = super.getUserAgent( method );
129 if ( userAgent == null )
130 {
131 return DEFAULT_USER_AGENT;
132 }
133 return userAgent;
134 }
135
136
137
138
139
140
141
142
143
144 protected void mkdirs( String dir )
145 throws IOException
146 {
147 Repository repository = getRepository();
148 String basedir = repository.getBasedir();
149
150 String baseUrl = repository.getProtocol() + "://" + repository.getHost();
151 if ( repository.getPort() != WagonConstants.UNKNOWN_PORT )
152 {
153 baseUrl += ":" + repository.getPort();
154 }
155
156
157 String relpath = FileUtils.normalize( getPath( basedir, dir ) + "/" );
158
159 PathNavigator navigator = new PathNavigator( relpath );
160
161
162
163 int status = -1;
164 do
165 {
166 String url = baseUrl + "/" + navigator.getPath();
167 status = doMkCol( url );
168 if ( status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED
169 || status == HttpStatus.SC_METHOD_NOT_ALLOWED )
170 {
171 break;
172 }
173 }
174 while ( navigator.backward() );
175
176
177 while ( navigator.forward() )
178 {
179 String url = baseUrl + "/" + navigator.getPath();
180 status = doMkCol( url );
181 if ( status != HttpStatus.SC_OK && status != HttpStatus.SC_CREATED )
182 {
183 throw new IOException( "Unable to create collection: " + url + "; status code = " + status );
184 }
185 }
186 }
187
188 private int doMkCol( String url )
189 throws IOException
190 {
191 Repository repo = getRepository();
192 HttpHost targetHost = new HttpHost( repo.getHost(), repo.getPort(), repo.getProtocol() );
193 AuthScope targetScope = getBasicAuthScope().getScope( targetHost );
194
195 if ( getCredentialsProvider().getCredentials( targetScope ) != null )
196 {
197 BasicScheme targetAuth = new BasicScheme();
198 getAuthCache().put( targetHost, targetAuth );
199 }
200 HttpMkcol method = new HttpMkcol( url );
201 try ( CloseableHttpResponse closeableHttpResponse = execute( method ) )
202 {
203 return closeableHttpResponse.getStatusLine().getStatusCode();
204 }
205 catch ( HttpException e )
206 {
207 throw new IOException( e.getMessage(), e );
208 }
209 finally
210 {
211 if ( method != null )
212 {
213 method.releaseConnection();
214 }
215 }
216 }
217
218
219
220
221
222
223
224
225
226
227 public void putDirectory( File sourceDirectory, String destinationDirectory )
228 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
229 {
230 for ( File file : sourceDirectory.listFiles() )
231 {
232 if ( file.isDirectory() )
233 {
234 putDirectory( file, destinationDirectory + "/" + file.getName() );
235 }
236 else
237 {
238 String target = destinationDirectory + "/" + file.getName();
239
240 put( file, target );
241 }
242 }
243 }
244 private boolean isDirectory( String url )
245 throws IOException, DavException
246 {
247 DavPropertyNameSet nameSet = new DavPropertyNameSet();
248 nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_RESOURCETYPE ) );
249
250 CloseableHttpResponse closeableHttpResponse = null;
251 HttpPropfind method = null;
252 try
253 {
254 method = new HttpPropfind( url, nameSet, DavConstants.DEPTH_0 );
255 closeableHttpResponse = execute( method );
256
257 if ( method.succeeded( closeableHttpResponse ) )
258 {
259 MultiStatus multiStatus = method.getResponseBodyAsMultiStatus( closeableHttpResponse );
260 MultiStatusResponse response = multiStatus.getResponses()[0];
261 DavPropertySet propertySet = response.getProperties( HttpStatus.SC_OK );
262 DavProperty<?> property = propertySet.get( DavConstants.PROPERTY_RESOURCETYPE );
263 if ( property != null )
264 {
265 Node node = (Node) property.getValue();
266 return node.getLocalName().equals( DavConstants.XML_COLLECTION );
267 }
268 }
269 return false;
270 }
271 catch ( HttpException e )
272 {
273 throw new IOException( e.getMessage(), e );
274 }
275 finally
276 {
277
278 if ( method != null )
279 {
280 method.releaseConnection();
281 }
282 if ( closeableHttpResponse != null )
283 {
284 closeableHttpResponse.close();
285 }
286 }
287 }
288
289 public List<String> getFileList( String destinationDirectory )
290 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
291 {
292 String repositoryUrl = repository.getUrl();
293 String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + destinationDirectory;
294
295 HttpPropfind method = null;
296 CloseableHttpResponse closeableHttpResponse = null;
297 try
298 {
299 if ( isDirectory( url ) )
300 {
301 DavPropertyNameSet nameSet = new DavPropertyNameSet();
302 nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_DISPLAYNAME ) );
303
304 method = new HttpPropfind( url, nameSet, DavConstants.DEPTH_1 );
305 closeableHttpResponse = execute( method );
306 if ( method.succeeded( closeableHttpResponse ) )
307 {
308 ArrayList<String> dirs = new ArrayList<>();
309 MultiStatus multiStatus = method.getResponseBodyAsMultiStatus( closeableHttpResponse );
310 for ( int i = 0; i < multiStatus.getResponses().length; i++ )
311 {
312 MultiStatusResponse response = multiStatus.getResponses()[i];
313 String entryUrl = response.getHref();
314 String fileName = PathUtils.filename( URLDecoder.decode( entryUrl ) );
315 if ( entryUrl.endsWith( "/" ) )
316 {
317 if ( i == 0 )
318 {
319
320
321
322 continue;
323 }
324
325
326 fileName = PathUtils.filename( PathUtils.dirname( URLDecoder.decode( entryUrl ) ) ) + "/";
327 }
328
329 if ( !StringUtils.isEmpty( fileName ) )
330 {
331 dirs.add( fileName );
332 }
333 }
334 return dirs;
335 }
336
337 if ( closeableHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND )
338 {
339 throw new ResourceDoesNotExistException( "Destination directory does not exist: " + url );
340 }
341 }
342 }
343 catch ( HttpException e )
344 {
345 throw new TransferFailedException( e.getMessage(), e );
346 }
347 catch ( DavException e )
348 {
349 throw new TransferFailedException( e.getMessage(), e );
350 }
351 catch ( IOException e )
352 {
353 throw new TransferFailedException( e.getMessage(), e );
354 }
355 finally
356 {
357
358 if ( method != null )
359 {
360 method.releaseConnection();
361 }
362 if ( closeableHttpResponse != null )
363 {
364 try
365 {
366 closeableHttpResponse.close();
367 }
368 catch ( IOException e )
369 {
370
371 }
372 }
373 }
374 throw new ResourceDoesNotExistException(
375 "Destination path exists but is not a " + "WebDAV collection (directory): " + url );
376 }
377
378 public String getURL( Repository repository )
379 {
380 String url = repository.getUrl();
381
382
383 for ( String[] entry : PROTOCOL_MAP )
384 {
385 String protocol = entry[0];
386 if ( url.startsWith( protocol ) )
387 {
388 return entry[1] + url.substring( protocol.length() );
389 }
390 }
391
392
393 return url;
394 }
395
396
397 public void put( File source, String resourceName )
398 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
399 {
400 try
401 {
402 super.put( source, resourceName );
403 }
404 catch ( TransferFailedException e )
405 {
406 if ( continueOnFailure )
407 {
408
409 System.out.println(
410 "WARN: Skip unable to transfer '" + resourceName + "' from '" + source.getPath() + "' due to "
411 + e.getMessage() );
412 }
413 else
414 {
415 throw e;
416 }
417 }
418 }
419 }