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