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.jackrabbit.webdav.DavConstants;
29 import org.apache.jackrabbit.webdav.DavException;
30 import org.apache.jackrabbit.webdav.MultiStatus;
31 import org.apache.jackrabbit.webdav.MultiStatusResponse;
32 import org.apache.jackrabbit.webdav.client.methods.HttpMkcol;
33 import org.apache.jackrabbit.webdav.client.methods.HttpPropfind;
34 import org.apache.jackrabbit.webdav.property.DavProperty;
35 import org.apache.jackrabbit.webdav.property.DavPropertyName;
36 import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
37 import org.apache.jackrabbit.webdav.property.DavPropertySet;
38 import org.apache.maven.wagon.PathUtils;
39 import org.apache.maven.wagon.ResourceDoesNotExistException;
40 import org.apache.maven.wagon.TransferFailedException;
41 import org.apache.maven.wagon.WagonConstants;
42 import org.apache.maven.wagon.authorization.AuthorizationException;
43 import org.apache.maven.wagon.repository.Repository;
44 import org.apache.maven.wagon.shared.http.AbstractHttpClientWagon;
45 import org.codehaus.plexus.util.FileUtils;
46 import org.codehaus.plexus.util.StringUtils;
47 import org.w3c.dom.Node;
48
49 import java.io.File;
50 import java.io.IOException;
51 import java.net.URLDecoder;
52 import java.util.ArrayList;
53 import java.util.List;
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 Repository repo = getRepository();
156 HttpHost targetHost = new HttpHost( repo.getHost(), repo.getPort(), repo.getProtocol() );
157 AuthScope targetScope = getBasicAuthScope().getScope( targetHost );
158
159 if ( getCredentialsProvider().getCredentials( targetScope ) != null )
160 {
161 BasicScheme targetAuth = new BasicScheme();
162 getAuthCache().put( targetHost, targetAuth );
163 }
164 HttpMkcol method = new HttpMkcol( url );
165 try ( CloseableHttpResponse closeableHttpResponse = execute( method ) )
166 {
167 return closeableHttpResponse.getStatusLine().getStatusCode();
168 }
169 catch ( HttpException e )
170 {
171 throw new IOException( e.getMessage(), e );
172 }
173 finally
174 {
175 if ( method != null )
176 {
177 method.releaseConnection();
178 }
179 }
180 }
181
182
183
184
185
186
187
188
189
190
191 public void putDirectory( File sourceDirectory, String destinationDirectory )
192 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
193 {
194 for ( File file : sourceDirectory.listFiles() )
195 {
196 if ( file.isDirectory() )
197 {
198 putDirectory( file, destinationDirectory + "/" + file.getName() );
199 }
200 else
201 {
202 String target = destinationDirectory + "/" + file.getName();
203
204 put( file, target );
205 }
206 }
207 }
208 private boolean isDirectory( String url )
209 throws IOException, DavException
210 {
211 DavPropertyNameSet nameSet = new DavPropertyNameSet();
212 nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_RESOURCETYPE ) );
213
214 CloseableHttpResponse closeableHttpResponse = null;
215 HttpPropfind method = null;
216 try
217 {
218 method = new HttpPropfind( url, nameSet, DavConstants.DEPTH_0 );
219 closeableHttpResponse = execute( method );
220
221 if ( method.succeeded( closeableHttpResponse ) )
222 {
223 MultiStatus multiStatus = method.getResponseBodyAsMultiStatus( closeableHttpResponse );
224 MultiStatusResponse response = multiStatus.getResponses()[0];
225 DavPropertySet propertySet = response.getProperties( HttpStatus.SC_OK );
226 DavProperty<?> property = propertySet.get( DavConstants.PROPERTY_RESOURCETYPE );
227 if ( property != null )
228 {
229 Node node = (Node) property.getValue();
230 return node.getLocalName().equals( DavConstants.XML_COLLECTION );
231 }
232 }
233 return false;
234 }
235 catch ( HttpException e )
236 {
237 throw new IOException( e.getMessage(), e );
238 }
239 finally
240 {
241
242 if ( method != null )
243 {
244 method.releaseConnection();
245 }
246 if ( closeableHttpResponse != null )
247 {
248 closeableHttpResponse.close();
249 }
250 }
251 }
252
253 public List<String> getFileList( String destinationDirectory )
254 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
255 {
256 String repositoryUrl = repository.getUrl();
257 String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + destinationDirectory;
258
259 HttpPropfind method = null;
260 CloseableHttpResponse closeableHttpResponse = null;
261 try
262 {
263 if ( isDirectory( url ) )
264 {
265 DavPropertyNameSet nameSet = new DavPropertyNameSet();
266 nameSet.add( DavPropertyName.create( DavConstants.PROPERTY_DISPLAYNAME ) );
267
268 method = new HttpPropfind( url, nameSet, DavConstants.DEPTH_1 );
269 closeableHttpResponse = execute( method );
270 if ( method.succeeded( closeableHttpResponse ) )
271 {
272 ArrayList<String> dirs = new ArrayList<>();
273 MultiStatus multiStatus = method.getResponseBodyAsMultiStatus( closeableHttpResponse );
274 for ( int i = 0; i < multiStatus.getResponses().length; i++ )
275 {
276 MultiStatusResponse response = multiStatus.getResponses()[i];
277 String entryUrl = response.getHref();
278 String fileName = PathUtils.filename( URLDecoder.decode( entryUrl ) );
279 if ( entryUrl.endsWith( "/" ) )
280 {
281 if ( i == 0 )
282 {
283
284
285
286 continue;
287 }
288
289
290 fileName = PathUtils.filename( PathUtils.dirname( URLDecoder.decode( entryUrl ) ) ) + "/";
291 }
292
293 if ( !StringUtils.isEmpty( fileName ) )
294 {
295 dirs.add( fileName );
296 }
297 }
298 return dirs;
299 }
300
301 if ( closeableHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND )
302 {
303 throw new ResourceDoesNotExistException( "Destination directory does not exist: " + url );
304 }
305 }
306 }
307 catch ( HttpException e )
308 {
309 throw new TransferFailedException( e.getMessage(), e );
310 }
311 catch ( DavException e )
312 {
313 throw new TransferFailedException( e.getMessage(), e );
314 }
315 catch ( IOException e )
316 {
317 throw new TransferFailedException( e.getMessage(), e );
318 }
319 finally
320 {
321
322 if ( method != null )
323 {
324 method.releaseConnection();
325 }
326 if ( closeableHttpResponse != null )
327 {
328 try
329 {
330 closeableHttpResponse.close();
331 }
332 catch ( IOException e )
333 {
334
335 }
336 }
337 }
338 throw new ResourceDoesNotExistException(
339 "Destination path exists but is not a " + "WebDAV collection (directory): " + url );
340 }
341
342 public String getURL( Repository repository )
343 {
344 String url = repository.getUrl();
345
346
347 for ( String[] entry : PROTOCOL_MAP )
348 {
349 String protocol = entry[0];
350 if ( url.startsWith( protocol ) )
351 {
352 return entry[1] + url.substring( protocol.length() );
353 }
354 }
355
356
357 return url;
358 }
359
360
361 public void put( File source, String resourceName )
362 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
363 {
364 try
365 {
366 super.put( source, resourceName );
367 }
368 catch ( TransferFailedException e )
369 {
370 if ( continueOnFailure )
371 {
372
373 System.out.println(
374 "WARN: Skip unable to transfer '" + resourceName + "' from '" + source.getPath() + "' due to "
375 + e.getMessage() );
376 }
377 else
378 {
379 throw e;
380 }
381 }
382 }
383 }