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