1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.provider.webdav4;
18
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.net.HttpURLConnection;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27
28 import org.apache.commons.vfs2.FileContentInfoFactory;
29 import org.apache.commons.vfs2.FileNotFolderException;
30 import org.apache.commons.vfs2.FileNotFoundException;
31 import org.apache.commons.vfs2.FileObject;
32 import org.apache.commons.vfs2.FileSystemException;
33 import org.apache.commons.vfs2.FileType;
34 import org.apache.commons.vfs2.NameScope;
35 import org.apache.commons.vfs2.provider.AbstractFileName;
36 import org.apache.commons.vfs2.provider.DefaultFileContent;
37 import org.apache.commons.vfs2.provider.GenericURLFileName;
38 import org.apache.commons.vfs2.provider.http4.Http4FileObject;
39 import org.apache.commons.vfs2.util.FileObjectUtils;
40 import org.apache.commons.vfs2.util.MonitorOutputStream;
41 import org.apache.commons.vfs2.util.URIUtils;
42 import org.apache.http.HttpEntity;
43 import org.apache.http.HttpResponse;
44 import org.apache.http.HttpStatus;
45 import org.apache.http.client.methods.HttpPut;
46 import org.apache.http.client.methods.HttpRequestBase;
47 import org.apache.http.client.methods.HttpUriRequest;
48 import org.apache.http.client.utils.DateUtils;
49 import org.apache.http.entity.ByteArrayEntity;
50 import org.apache.http.entity.ContentType;
51 import org.apache.jackrabbit.webdav.DavConstants;
52 import org.apache.jackrabbit.webdav.DavException;
53 import org.apache.jackrabbit.webdav.MultiStatus;
54 import org.apache.jackrabbit.webdav.MultiStatusResponse;
55 import org.apache.jackrabbit.webdav.client.methods.BaseDavRequest;
56 import org.apache.jackrabbit.webdav.client.methods.HttpCheckin;
57 import org.apache.jackrabbit.webdav.client.methods.HttpCheckout;
58 import org.apache.jackrabbit.webdav.client.methods.HttpDelete;
59 import org.apache.jackrabbit.webdav.client.methods.HttpMkcol;
60 import org.apache.jackrabbit.webdav.client.methods.HttpMove;
61 import org.apache.jackrabbit.webdav.client.methods.HttpPropfind;
62 import org.apache.jackrabbit.webdav.client.methods.HttpProppatch;
63 import org.apache.jackrabbit.webdav.client.methods.HttpVersionControl;
64 import org.apache.jackrabbit.webdav.property.DavProperty;
65 import org.apache.jackrabbit.webdav.property.DavPropertyIterator;
66 import org.apache.jackrabbit.webdav.property.DavPropertyName;
67 import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
68 import org.apache.jackrabbit.webdav.property.DavPropertySet;
69 import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
70 import org.apache.jackrabbit.webdav.version.DeltaVConstants;
71 import org.apache.jackrabbit.webdav.version.VersionControlledResource;
72 import org.apache.jackrabbit.webdav.xml.Namespace;
73 import org.w3c.dom.Node;
74
75
76
77
78
79
80 public class Webdav4FileObject extends Http4FileObject<Webdav4FileSystem> {
81
82
83
84
85 private static final Webdav4FileObject/Webdav4FileObject.html#Webdav4FileObject">Webdav4FileObject[] EMPTY_ARRAY = new Webdav4FileObject[0];
86
87
88
89
90
91
92 private class WebdavOutputStream extends MonitorOutputStream {
93 private final Webdav4FileObject file;
94
95 public WebdavOutputStream(final Webdav4FileObject file) {
96 super(new ByteArrayOutputStream());
97 this.file = file;
98 }
99
100 private boolean createVersion(final String urlStr) {
101 try {
102 final HttpVersionControl request = new HttpVersionControl(urlStr);
103 setupRequest(request);
104 executeRequest(request);
105 return true;
106 } catch (final Exception ex) {
107 return false;
108 }
109 }
110
111
112
113
114 @Override
115 protected void onClose() throws IOException {
116 final HttpEntity entity = new ByteArrayEntity(((ByteArrayOutputStream) out).toByteArray());
117 final GenericURLFileName fileName = (GenericURLFileName) getName();
118 final String urlStr = toUrlString(fileName);
119 if (builder.isVersioning(getFileSystem().getFileSystemOptions())) {
120 DavPropertySet set = null;
121 boolean fileExists = true;
122 boolean isCheckedIn = true;
123 try {
124 set = getPropertyNames(fileName);
125 } catch (final FileNotFoundException fnfe) {
126 fileExists = false;
127 }
128 if (fileExists && set != null) {
129 if (set.contains(VersionControlledResource.CHECKED_OUT)) {
130 isCheckedIn = false;
131 } else if (!set.contains(VersionControlledResource.CHECKED_IN)) {
132 DavProperty<?> prop = set.get(VersionControlledResource.AUTO_VERSION);
133 if (prop != null) {
134 prop = getProperty(fileName, VersionControlledResource.AUTO_VERSION);
135 if (DeltaVConstants.XML_CHECKOUT_CHECKIN.equals(prop.getValue())) {
136 createVersion(urlStr);
137 }
138 }
139 }
140 }
141 if (fileExists && isCheckedIn) {
142 try {
143 final HttpCheckout request = new HttpCheckout(urlStr);
144 setupRequest(request);
145 executeRequest(request);
146 isCheckedIn = false;
147 } catch (final FileSystemException ex) {
148 log(ex);
149 }
150 }
151
152 try {
153 final HttpPut request = new HttpPut(urlStr);
154 request.setEntity(entity);
155 setupRequest(request);
156 executeRequest(request);
157 setUserName(fileName, urlStr);
158 } catch (final FileSystemException ex) {
159 if (!isCheckedIn) {
160 try {
161 final HttpCheckin request = new HttpCheckin(urlStr);
162 setupRequest(request);
163 executeRequest(request);
164 isCheckedIn = true;
165 } catch (final Exception e) {
166
167 log(e);
168 }
169 throw ex;
170 }
171 }
172 if (!fileExists) {
173 createVersion(urlStr);
174 try {
175 final DavPropertySet props = getPropertyNames(fileName);
176 isCheckedIn = !props.contains(VersionControlledResource.CHECKED_OUT);
177 } catch (final FileNotFoundException fnfe) {
178 log(fnfe);
179 }
180 }
181 if (!isCheckedIn) {
182 final HttpCheckin request = new HttpCheckin(urlStr);
183 setupRequest(request);
184 executeRequest(request);
185 }
186 } else {
187 final HttpPut request = new HttpPut(urlStr);
188 request.setEntity(entity);
189 setupRequest(request);
190 executeRequest(request);
191 try {
192 setUserName(fileName, urlStr);
193 } catch (final IOException e) {
194
195 log(e);
196 }
197 }
198 ((DefaultFileContent) this.file.getContent()).resetAttributes();
199 }
200
201 private void setUserName(final GenericURLFileName fileName, final String urlStr) throws IOException {
202 final DavPropertySet setProperties = new DavPropertySet();
203 final DavPropertyNameSet removeProperties = new DavPropertyNameSet();
204 String name = builder.getCreatorName(getFileSystem().getFileSystemOptions());
205 final String userName = fileName.getUserName();
206 if (name == null) {
207 name = userName;
208 } else if (userName != null) {
209 final String comment = "Modified by user " + userName;
210 setProperties.add(new DefaultDavProperty<>(DeltaVConstants.COMMENT, comment));
211 }
212 setProperties.add(new DefaultDavProperty<>(DeltaVConstants.CREATOR_DISPLAYNAME, name));
213 final HttpProppatch request = new HttpProppatch(urlStr, setProperties, removeProperties);
214 setupRequest(request);
215 executeRequest(request);
216 }
217 }
218
219
220 public static final DavPropertyName RESPONSE_CHARSET = DavPropertyName.create("response-charset");
221
222
223 private final Webdav4FileSystemConfigBuilder builder;
224
225
226 protected Webdav4FileObject(final AbstractFileName name, final Webdav4FileSystem fileSystem)
227 throws FileSystemException {
228 this(name, fileSystem, Webdav4FileSystemConfigBuilder.getInstance());
229 }
230
231 void log(Exception ex) {
232
233 }
234
235 protected Webdav4FileObject(final AbstractFileName name, final Webdav4FileSystem fileSystem,
236 final Webdav4FileSystemConfigBuilder builder) throws FileSystemException {
237 super(name, fileSystem, builder);
238 this.builder = builder;
239 }
240
241
242
243
244 @Override
245 protected void doCreateFolder() throws Exception {
246 final HttpMkcol request = new HttpMkcol(toUrlString((GenericURLFileName) getName()));
247 setupRequest(request);
248 try {
249 executeRequest(request);
250 } catch (final FileSystemException fse) {
251 throw new FileSystemException("vfs.provider.webdav/create-collection.error", getName(), fse);
252 }
253 }
254
255
256
257
258 @Override
259 protected void doDelete() throws Exception {
260 final HttpDelete request = new HttpDelete(toUrlString((GenericURLFileName) getName()));
261 setupRequest(request);
262 executeRequest(request);
263 }
264
265
266
267
268 @Override
269 protected Map<String, Object> doGetAttributes() throws Exception {
270 final Map<String, Object> attributes = new HashMap<>();
271 try {
272 final GenericURLFileName fileName = (GenericURLFileName) getName();
273 DavPropertySet properties = getProperties(fileName, DavConstants.PROPFIND_ALL_PROP,
274 new DavPropertyNameSet(), false);
275 final DavPropertyIterator iter = properties.iterator();
276 while (iter.hasNext()) {
277 final DavProperty<?> property = iter.nextProperty();
278 attributes.put(property.getName().toString(), property.getValue());
279 }
280 properties = getPropertyNames(fileName);
281 final DavPropertyIterator iter2 = properties.iterator();
282 while (iter2.hasNext()) {
283 DavProperty<?> property = iter2.nextProperty();
284 if (!attributes.containsKey(property.getName().getName())) {
285 property = getProperty(fileName, property.getName());
286 if (property != null) {
287 final Object name = property.getName();
288 final Object value = property.getValue();
289 if (name != null && value != null) {
290 attributes.put(name.toString(), value);
291 }
292 }
293 }
294 }
295 return attributes;
296 } catch (final Exception e) {
297 throw new FileSystemException("vfs.provider.webdav/get-attributes.error", getName(), e);
298 }
299 }
300
301
302
303
304 @Override
305 protected long doGetContentSize() throws Exception {
306 final DavProperty<?> property = getProperty((GenericURLFileName) getName(), DavConstants.PROPERTY_GETCONTENTLENGTH);
307 if (property != null) {
308 final String value = (String) property.getValue();
309 return Long.parseLong(value);
310 }
311 return 0;
312 }
313
314
315
316
317
318 @Override
319 protected long doGetLastModifiedTime() throws Exception {
320 final DavProperty<?> property = getProperty((GenericURLFileName) getName(), DavConstants.PROPERTY_GETLASTMODIFIED);
321 if (property != null) {
322 final String value = (String) property.getValue();
323 return DateUtils.parseDate(value).getTime();
324 }
325 return 0;
326 }
327
328 @Override
329 protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
330 return new WebdavOutputStream(this);
331 }
332
333
334
335
336
337 @Override
338 protected FileType doGetType() throws Exception {
339 try {
340 return isDirectory((GenericURLFileName) getName()) ? FileType.FOLDER : FileType.FILE;
341 } catch (final FileNotFolderException | FileNotFoundException fnfe) {
342 return FileType.IMAGINARY;
343 }
344
345 }
346
347
348
349
350
351
352
353
354
355
356 @Override
357 protected boolean doIsWriteable() throws Exception {
358 return true;
359 }
360
361
362
363
364 @Override
365 protected String[] doListChildren() throws Exception {
366
367 return null;
368 }
369
370
371
372
373 @Override
374 protected FileObject[] doListChildrenResolved() throws Exception {
375 HttpPropfind request = null;
376 try {
377 final GenericURLFileName name = (GenericURLFileName) getName();
378 if (isDirectory(name)) {
379 final DavPropertyNameSet nameSet = new DavPropertyNameSet();
380 nameSet.add(DavPropertyName.create(DavConstants.PROPERTY_DISPLAYNAME));
381
382 request = new HttpPropfind(toUrlString(name), nameSet, DavConstants.DEPTH_1);
383
384 final HttpResponse res = executeRequest(request);
385 final List<Webdav4FileObject> vfs = new ArrayList<>();
386 if (request.succeeded(res)) {
387 final MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(res).getResponses();
388
389 for (final MultiStatusResponse response : responses) {
390 if (isCurrentFile(response.getHref(), name)) {
391 continue;
392 }
393 final String resourceName = resourceName(response.getHref());
394 if (!resourceName.isEmpty()) {
395 final Webdav4FileObject./../../../org/apache/commons/vfs2/provider/webdav4/Webdav4FileObject.html#Webdav4FileObject">Webdav4FileObject fo = (Webdav4FileObject) FileObjectUtils.getAbstractFileObject(
396 getFileSystem().resolveFile(getFileSystem().getFileSystemManager()
397 .resolveName(getName(), resourceName, NameScope.CHILD)));
398 vfs.add(fo);
399 }
400 }
401 }
402 return vfs.toArray(EMPTY_ARRAY);
403 }
404 throw new FileNotFolderException(getName());
405 } catch (final FileNotFolderException fnfe) {
406 throw fnfe;
407 } catch (final DavException | IOException e) {
408 throw new FileSystemException(e.getMessage(), e);
409 } finally {
410 if (request != null) {
411 request.releaseConnection();
412 }
413 }
414 }
415
416
417
418
419 @Override
420 protected void doRename(final FileObject newFile) throws Exception {
421 final String url = URIUtils.encodePath(toUrlString((GenericURLFileName) getName()));
422 final String dest = toUrlString((GenericURLFileName) newFile.getName(), false);
423 final HttpMove request = new HttpMove(url, dest, false);
424 setupRequest(request);
425 executeRequest(request);
426 }
427
428
429
430
431 @Override
432 protected void doSetAttribute(final String attrName, final Object value) throws Exception {
433 try {
434 final GenericURLFileName fileName = (GenericURLFileName) getName();
435 final String urlStr = toUrlString(fileName);
436 final DavPropertySet properties = new DavPropertySet();
437 final DavPropertyNameSet propertyNameSet = new DavPropertyNameSet();
438 final DavProperty<Object> property = new DefaultDavProperty<>(attrName, value, Namespace.EMPTY_NAMESPACE);
439 if (value != null) {
440 properties.add(property);
441 } else {
442 propertyNameSet.add(property.getName());
443 }
444
445 final HttpProppatch request = new HttpProppatch(urlStr, properties, propertyNameSet);
446 setupRequest(request);
447 final HttpResponse response = executeRequest(request);
448 if (!request.succeeded(response)) {
449 throw new FileSystemException("Property '" + attrName + "' could not be set.");
450 }
451 } catch (final FileSystemException fse) {
452 throw fse;
453 } catch (final Exception e) {
454 throw new FileSystemException("vfs.provider.webdav/set-attributes", e, getName(), attrName);
455 }
456 }
457
458 private HttpResponse executeRequest(final HttpUriRequest request) throws FileSystemException {
459 final HttpResponse response;
460
461 try {
462 response = executeHttpUriRequest(request);
463 final int status = response.getStatusLine().getStatusCode();
464 if (status == HttpURLConnection.HTTP_NOT_FOUND || status == HttpURLConnection.HTTP_GONE) {
465 throw new FileNotFoundException(request.getURI());
466 }
467
468 if (request instanceof BaseDavRequest) {
469 ((BaseDavRequest) request).checkSuccess(response);
470 }
471
472 return response;
473 } catch (final FileSystemException fse) {
474 throw fse;
475 } catch (final IOException e) {
476 throw new FileSystemException(e);
477 } catch (final DavException e) {
478 throw ExceptionConverter.generate(e);
479 } finally {
480 if (request instanceof HttpRequestBase) {
481 ((HttpRequestBase) request).releaseConnection();
482 }
483 }
484 }
485
486 @Override
487 protected FileContentInfoFactory getFileContentInfoFactory() {
488 return new Webdav4FileContentInfoFactory();
489 }
490
491 DavPropertySet getProperties(final GenericURLFileName name) throws FileSystemException {
492 return getProperties(name, DavConstants.PROPFIND_ALL_PROP, new DavPropertyNameSet(), false);
493 }
494
495 DavPropertySet getProperties(final GenericURLFileName name, final DavPropertyNameSet nameSet, final boolean addEncoding)
496 throws FileSystemException {
497 return getProperties(name, DavConstants.PROPFIND_BY_PROPERTY, nameSet, addEncoding);
498 }
499
500 DavPropertySet getProperties(final GenericURLFileName name, final int type, final DavPropertyNameSet nameSet,
501 final boolean addEncoding) throws FileSystemException {
502 try {
503 final String urlStr = toUrlString(name);
504 final HttpPropfind request = new HttpPropfind(urlStr, type, nameSet, DavConstants.DEPTH_0);
505 setupRequest(request);
506 final HttpResponse res = executeRequest(request);
507 if (request.succeeded(res)) {
508 final MultiStatus multiStatus = request.getResponseBodyAsMultiStatus(res);
509 final MultiStatusResponse response = multiStatus.getResponses()[0];
510 final DavPropertySet props = response.getProperties(HttpStatus.SC_OK);
511 if (addEncoding) {
512 final ContentType resContentType = ContentType.getOrDefault(res.getEntity());
513 final DavProperty<String> prop = new DefaultDavProperty<>(RESPONSE_CHARSET,
514 resContentType.getCharset().name());
515 props.add(prop);
516 }
517 return props;
518 }
519 return new DavPropertySet();
520 } catch (final FileSystemException fse) {
521 throw fse;
522 } catch (final Exception e) {
523 throw new FileSystemException("vfs.provider.webdav/get-property.error", e, getName(), name, type,
524 nameSet.getContent(), addEncoding);
525 }
526 }
527
528 DavProperty<?> getProperty(final GenericURLFileName fileName, final DavPropertyName name) throws FileSystemException {
529 final DavPropertyNameSet nameSet = new DavPropertyNameSet();
530 nameSet.add(name);
531 final DavPropertySet propertySet = getProperties(fileName, nameSet, false);
532 return propertySet.get(name);
533 }
534
535 DavProperty<?> getProperty(final GenericURLFileName fileName, final String property) throws FileSystemException {
536 return getProperty(fileName, DavPropertyName.create(property));
537 }
538
539 DavPropertySet getPropertyNames(final GenericURLFileName name) throws FileSystemException {
540 return getProperties(name, DavConstants.PROPFIND_PROPERTY_NAMES, new DavPropertyNameSet(), false);
541 }
542
543
544
545
546
547
548
549 private String hrefString(final GenericURLFileName name) {
550 try {
551 final GenericURLFileName newFile = new GenericURLFileName(getInternalURI().getScheme(), name.getHostName(), name.getPort(), name.getDefaultPort(),
552 null, null, name.getPath(), name.getType(), name.getQueryString());
553 return newFile.getURIEncoded(this.getUrlCharset());
554 } catch (final Exception e) {
555 return name.getURI();
556 }
557 }
558
559 private boolean isCurrentFile(final String href, final GenericURLFileName fileName) {
560 String name = hrefString(fileName);
561 if (href.endsWith("/") && !name.endsWith("/")) {
562 name += "/";
563 }
564 return href.equals(name) || href.equals(fileName.getPath());
565 }
566
567 private boolean isDirectory(final GenericURLFileName name) throws IOException {
568 try {
569 final DavProperty<?> property = getProperty(name, DavConstants.PROPERTY_RESOURCETYPE);
570 final Node node;
571 if (property != null && (node = (Node) property.getValue()) != null) {
572 return node.getLocalName().equals(DavConstants.XML_COLLECTION);
573 }
574 return false;
575 } catch (final FileNotFoundException fse) {
576 throw new FileNotFolderException(name);
577 }
578 }
579
580
581
582
583
584
585
586 private String resourceName(String path) {
587 if (path.endsWith("/")) {
588 path = path.substring(0, path.length() - 1);
589 }
590 final int i = path.lastIndexOf("/");
591 return i >= 0 ? path.substring(i + 1) : path;
592 }
593
594 private void setupRequest(final HttpUriRequest request) {
595
596 request.addHeader("Cache-control", "no-cache");
597 request.addHeader("Cache-store", "no-store");
598 request.addHeader("Pragma", "no-cache");
599 request.addHeader("Expires", "0");
600 }
601
602
603
604
605
606
607
608 String toUrlString(final GenericURLFileName name) {
609 return toUrlString(name, true);
610 }
611
612
613
614
615
616
617
618
619 private String toUrlString(final GenericURLFileName name, final boolean includeUserInfo) {
620 String user = null;
621 String password = null;
622 if (includeUserInfo) {
623 user = name.getUserName();
624 password = name.getPassword();
625 }
626 try {
627 final GenericURLFileName newFile = new GenericURLFileName(getInternalURI().getScheme(), name.getHostName(), name.getPort(), name.getDefaultPort(),
628 user, password, name.getPath(), name.getType(), name.getQueryString());
629 return newFile.getURIEncoded(this.getUrlCharset());
630 } catch (final Exception e) {
631 return name.getURI();
632 }
633 }
634 }