001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.vfs2.provider.webdav4;
018
019import java.io.ByteArrayOutputStream;
020import java.io.IOException;
021import java.io.OutputStream;
022import java.net.HttpURLConnection;
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.commons.vfs2.FileContentInfoFactory;
029import org.apache.commons.vfs2.FileNotFolderException;
030import org.apache.commons.vfs2.FileNotFoundException;
031import org.apache.commons.vfs2.FileObject;
032import org.apache.commons.vfs2.FileSystemException;
033import org.apache.commons.vfs2.FileType;
034import org.apache.commons.vfs2.NameScope;
035import org.apache.commons.vfs2.provider.AbstractFileName;
036import org.apache.commons.vfs2.provider.DefaultFileContent;
037import org.apache.commons.vfs2.provider.GenericURLFileName;
038import org.apache.commons.vfs2.provider.http4.Http4FileObject;
039import org.apache.commons.vfs2.util.FileObjectUtils;
040import org.apache.commons.vfs2.util.MonitorOutputStream;
041import org.apache.http.HttpEntity;
042import org.apache.http.HttpResponse;
043import org.apache.http.HttpStatus;
044import org.apache.http.client.methods.CloseableHttpResponse;
045import org.apache.http.client.methods.HttpPut;
046import org.apache.http.client.methods.HttpUriRequest;
047import org.apache.http.client.utils.DateUtils;
048import org.apache.http.entity.ByteArrayEntity;
049import org.apache.http.entity.ContentType;
050import org.apache.http.util.EntityUtils;
051import org.apache.jackrabbit.webdav.DavConstants;
052import org.apache.jackrabbit.webdav.DavException;
053import org.apache.jackrabbit.webdav.MultiStatus;
054import org.apache.jackrabbit.webdav.MultiStatusResponse;
055import org.apache.jackrabbit.webdav.client.methods.BaseDavRequest;
056import org.apache.jackrabbit.webdav.client.methods.HttpCheckin;
057import org.apache.jackrabbit.webdav.client.methods.HttpCheckout;
058import org.apache.jackrabbit.webdav.client.methods.HttpDelete;
059import org.apache.jackrabbit.webdav.client.methods.HttpMkcol;
060import org.apache.jackrabbit.webdav.client.methods.HttpMove;
061import org.apache.jackrabbit.webdav.client.methods.HttpPropfind;
062import org.apache.jackrabbit.webdav.client.methods.HttpProppatch;
063import org.apache.jackrabbit.webdav.client.methods.HttpVersionControl;
064import org.apache.jackrabbit.webdav.property.DavProperty;
065import org.apache.jackrabbit.webdav.property.DavPropertyName;
066import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
067import org.apache.jackrabbit.webdav.property.DavPropertySet;
068import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
069import org.apache.jackrabbit.webdav.version.DeltaVConstants;
070import org.apache.jackrabbit.webdav.version.VersionControlledResource;
071import org.apache.jackrabbit.webdav.xml.Namespace;
072import org.w3c.dom.Node;
073
074/**
075 * A WebDAV file.
076 *
077 * @since 2.5.0
078 */
079public class Webdav4FileObject extends Http4FileObject<Webdav4FileSystem> {
080
081    /**
082     * An OutputStream that writes to a WebDAV resource.
083     * <p>
084     * TODO - Use piped stream to avoid temporary file.
085     * </p>
086     */
087    private class WebdavOutputStream extends MonitorOutputStream {
088        private final Webdav4FileObject file;
089
090        WebdavOutputStream(final Webdav4FileObject file) {
091            super(new ByteArrayOutputStream());
092            this.file = file;
093        }
094
095        private boolean createVersion(final String urlStr) {
096            try {
097                final HttpVersionControl request = setupRequest(new HttpVersionControl(urlStr));
098                // AutoClose the underlying HTTP connection which is held by the response object
099                try (CloseableHttpResponse unused = (CloseableHttpResponse) executeRequest(request)) {
100                    return true;
101                }
102            } catch (final Exception ex) {
103                return false;
104            }
105        }
106
107        /**
108         * Called after this stream is closed.
109         */
110        @Override
111        protected void onClose() throws IOException {
112            final HttpEntity entity = new ByteArrayEntity(((ByteArrayOutputStream) out).toByteArray());
113            final GenericURLFileName fileName = (GenericURLFileName) 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 HttpCheckout request = setupRequest(new HttpCheckout(urlStr));
140                        // AutoClose the underlying HTTP connection which is held by the response object
141                        try (CloseableHttpResponse unused = (CloseableHttpResponse) executeRequest(request)) {
142                            isCheckedIn = false;
143                        }
144                    } catch (final FileSystemException ex) {
145                        log(ex);
146                    }
147                }
148
149                try {
150                    final HttpPut request = new HttpPut(urlStr);
151                    request.setEntity(entity);
152                    setupRequest(request);
153                    // AutoClose the underlying HTTP connection which is held by the response object
154                    try (CloseableHttpResponse unused = (CloseableHttpResponse) executeRequest(request)) {
155                        setUserName(fileName, urlStr);
156                    }
157                } catch (final FileSystemException ex) {
158                    if (!isCheckedIn) {
159                        try {
160                            final HttpCheckin request = new HttpCheckin(urlStr);
161                            setupRequest(request);
162                            // AutoClose the underlying HTTP connection which is held by the response object
163                            try (CloseableHttpResponse unused = (CloseableHttpResponse) executeRequest(request)) {
164                                isCheckedIn = true;
165                            }
166                        } catch (final Exception e) {
167                            // Going to throw original.
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 HttpCheckin request = new HttpCheckin(urlStr);
184                    setupRequest(request);
185                    // AutoClose the underlying HTTP connection which is held by the response object
186                    try (CloseableHttpResponse unused = (CloseableHttpResponse) executeRequest(request)) {
187                        isCheckedIn = true;
188                    }
189                }
190            } else {
191                final HttpPut request = new HttpPut(urlStr);
192                request.setEntity(entity);
193                setupRequest(request);
194                // AutoClose the underlying HTTP connection which is held by the response object
195                try (CloseableHttpResponse unused = (CloseableHttpResponse) executeRequest(request)) {
196                    try {
197                        setUserName(fileName, urlStr);
198                    } catch (final IOException e) {
199                        // Unable to set the user name.
200                        log(e);
201                    }
202                }
203            }
204            ((DefaultFileContent) file.getContent()).resetAttributes();
205        }
206
207        private void setUserName(final GenericURLFileName fileName, final String urlStr) throws IOException {
208            final DavPropertySet setProperties = new DavPropertySet();
209            final DavPropertyNameSet removeProperties = new DavPropertyNameSet();
210            String name = builder.getCreatorName(getFileSystem().getFileSystemOptions());
211            final String userName = fileName.getUserName();
212            if (name == null) {
213                name = userName;
214            } else if (userName != null) {
215                final String comment = "Modified by user " + userName;
216                setProperties.add(new DefaultDavProperty<>(DeltaVConstants.COMMENT, comment));
217            }
218            setProperties.add(new DefaultDavProperty<>(DeltaVConstants.CREATOR_DISPLAYNAME, name));
219            final HttpProppatch request = setupRequest(new HttpProppatch(urlStr, setProperties, removeProperties));
220            // AutoClose the underlying HTTP connection which is held by the response object
221            try (CloseableHttpResponse res = (CloseableHttpResponse) executeRequest(request)) {
222                // TODO: workaround due to PMD violation 'Empty try body - you could rename the resource to 'ignored'
223                request.succeeded(res);
224            }
225        }
226    }
227
228    /** The character set property name. */
229    public static final DavPropertyName RESPONSE_CHARSET = DavPropertyName.create("response-charset");
230
231    /**
232     * An empty immutable {@code Webdav4FileObject} array.
233     */
234    private static final Webdav4FileObject[] EMPTY_ARRAY = {};
235
236    /** The FileSystemConfigBuilder */
237    private final Webdav4FileSystemConfigBuilder builder;
238
239    /**
240     * Constructs a new instance.
241     *
242     * @param name file name.
243     * @param fileSystem file system.
244     * @throws FileSystemException if any error occurs
245     */
246    protected Webdav4FileObject(final AbstractFileName name, final Webdav4FileSystem fileSystem)
247            throws FileSystemException {
248        this(name, fileSystem, Webdav4FileSystemConfigBuilder.getInstance());
249    }
250
251    /**
252     * Constructs a new instance.
253     *
254     * @param name file name.
255     * @param fileSystem file system.
256     * @param builder builds the file system configuration.
257     * @throws FileSystemException if any error occurs
258     */
259    protected Webdav4FileObject(final AbstractFileName name, final Webdav4FileSystem fileSystem,
260            final Webdav4FileSystemConfigBuilder builder) throws FileSystemException {
261        super(name, fileSystem, builder);
262        this.builder = builder;
263    }
264
265    /**
266     * Creates this file as a folder.
267     */
268    @Override
269    protected void doCreateFolder() throws Exception {
270        final HttpMkcol request = setupRequest(new HttpMkcol(toUrlString((GenericURLFileName) getName())));
271        try {
272            // AutoClose the underlying HTTP connection which is held by the response object
273            try (CloseableHttpResponse res = (CloseableHttpResponse) executeRequest(request)) {
274                // TODO: workaround due to PMD violation 'Empty try body - you could rename the resource to 'ignored'
275                request.succeeded(res);
276            }
277        } catch (final FileSystemException fse) {
278            throw new FileSystemException("vfs.provider.webdav/create-collection.error", getName(), fse);
279        }
280    }
281
282    /**
283     * Deletes the file.
284     */
285    @Override
286    protected void doDelete() throws Exception {
287        final HttpDelete request = setupRequest(new HttpDelete(toUrlString((GenericURLFileName) getName())));
288        // AutoClose the underlying HTTP connection which is held by the response object
289        try (CloseableHttpResponse res = (CloseableHttpResponse) executeRequest(request)) {
290            // TODO: workaround due to PMD violation 'Empty try body - you could rename the resource to 'ignored'
291            request.succeeded(res);
292        }
293    }
294
295    /**
296     * Returns the properties of the Webdav resource.
297     */
298    @Override
299    protected Map<String, Object> doGetAttributes() throws Exception {
300        final Map<String, Object> attributes = new HashMap<>();
301        try {
302            final GenericURLFileName fileName = (GenericURLFileName) getName();
303            DavPropertySet properties = getProperties(fileName, DavConstants.PROPFIND_ALL_PROP,
304                    new DavPropertyNameSet(), false);
305            for (final DavProperty<?> property : properties) {
306                attributes.put(property.getName().toString(), property.getValue());
307            }
308            properties = getPropertyNames(fileName);
309            for (DavProperty<?> property : properties) {
310                if (!attributes.containsKey(property.getName().getName())) {
311                    property = getProperty(fileName, property.getName());
312                    if (property != null) {
313                        final Object name = property.getName();
314                        final Object value = property.getValue();
315                        if (name != null && value != null) {
316                            attributes.put(name.toString(), value);
317                        }
318                    }
319                }
320            }
321            return attributes;
322        } catch (final Exception e) {
323            throw new FileSystemException("vfs.provider.webdav/get-attributes.error", getName(), e);
324        }
325    }
326
327    /**
328     * Returns the size of the file content (in bytes).
329     */
330    @Override
331    protected long doGetContentSize() throws Exception {
332        final DavProperty<?> property = getProperty((GenericURLFileName) getName(), DavConstants.PROPERTY_GETCONTENTLENGTH);
333        if (property != null) {
334            final String value = (String) property.getValue();
335            return Long.parseLong(value);
336        }
337        return 0;
338    }
339
340    /**
341     * Returns the last modified time of this file. Is only called if {@link #doGetType} does not return
342     * {@link FileType#IMAGINARY}.
343     */
344    @Override
345    protected long doGetLastModifiedTime() throws Exception {
346        final DavProperty<?> property = getProperty((GenericURLFileName) getName(), DavConstants.PROPERTY_GETLASTMODIFIED);
347        if (property != null) {
348            final String value = (String) property.getValue();
349            return DateUtils.parseDate(value).getTime();
350        }
351        return 0;
352    }
353
354    @Override
355    protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
356        return new WebdavOutputStream(this);
357    }
358
359    /**
360     * Determines the type of this file. Must not return null. The return value of this method is cached, so the
361     * implementation can be expensive.
362     */
363    @Override
364    protected FileType doGetType() throws Exception {
365        try {
366            return isDirectory((GenericURLFileName) getName()) ? FileType.FOLDER : FileType.FILE;
367        } catch (final FileNotFolderException | FileNotFoundException fnfe) {
368            return FileType.IMAGINARY;
369        }
370
371    }
372
373    /**
374     * Determines if this file can be written to. Is only called if {@link #doGetType} does not return
375     * {@link FileType#IMAGINARY}.
376     * <p>
377     * This implementation always returns true.
378     *
379     * @return true if the file is writable.
380     * @throws Exception if an error occurs.
381     */
382    @Override
383    protected boolean doIsWriteable() throws Exception {
384        return true;
385    }
386
387    /**
388     * Lists the children of the file.
389     */
390    @Override
391    protected String[] doListChildren() throws Exception {
392        // use doListChildrenResolved for performance
393        return null;
394    }
395
396    /**
397     * Lists the children of the file.
398     */
399    @Override
400    protected FileObject[] doListChildrenResolved() throws Exception {
401        HttpPropfind request = null;
402        try {
403            final GenericURLFileName name = (GenericURLFileName) getName();
404            if (isDirectory(name)) {
405                final DavPropertyNameSet nameSet = new DavPropertyNameSet();
406                nameSet.add(DavPropertyName.create(DavConstants.PROPERTY_DISPLAYNAME));
407
408                request = new HttpPropfind(toUrlString(name), nameSet, DavConstants.DEPTH_1);
409
410                try (CloseableHttpResponse res = (CloseableHttpResponse) executeRequest(request)) {
411                    final List<Webdav4FileObject> vfs = new ArrayList<>();
412                    if (request.succeeded(res)) {
413                        final MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(res).getResponses();
414
415                        for (final MultiStatusResponse response : responses) {
416                            if (isCurrentFile(response.getHref(), name)) {
417                                continue;
418                            }
419                            final String resourceName = resourceName(response.getHref());
420                            if (!resourceName.isEmpty()) {
421                                final Webdav4FileObject fo = (Webdav4FileObject) FileObjectUtils.getAbstractFileObject(
422                                        getFileSystem().resolveFile(getFileSystem().getFileSystemManager()
423                                                .resolveName(getName(), resourceName, NameScope.CHILD)));
424                                vfs.add(fo);
425                            }
426                        }
427                    }
428                    return vfs.toArray(EMPTY_ARRAY);
429                }
430            }
431            throw new FileNotFolderException(getName());
432        } catch (final FileNotFolderException fnfe) {
433            throw fnfe;
434        } catch (final DavException | IOException e) {
435            throw new FileSystemException(e.getMessage(), e);
436        }
437    }
438
439    /**
440     * Rename the file.
441     */
442    @Override
443    protected void doRename(final FileObject newFile) throws Exception {
444        final String url = toUrlString((GenericURLFileName) getName());
445        final String dest = toUrlString((GenericURLFileName) newFile.getName(), false);
446        final HttpMove request = setupRequest(new HttpMove(url, dest, false));
447        // AutoClose the underlying HTTP connection which is held by the response object
448        try (CloseableHttpResponse res = (CloseableHttpResponse) executeRequest(request)) {
449            // TODO: workaround due to PMD violation 'Empty try body - you could rename the resource to 'ignored'
450            request.succeeded(res);
451        }
452    }
453
454    /**
455     * Sets an attribute of this file. Is only called if {@link #doGetType} does not return {@link FileType#IMAGINARY}.
456     */
457    @Override
458    protected void doSetAttribute(final String attrName, final Object value) throws Exception {
459        try {
460            final GenericURLFileName fileName = (GenericURLFileName) getName();
461            final String urlStr = toUrlString(fileName);
462            final DavPropertySet properties = new DavPropertySet();
463            final DavPropertyNameSet propertyNameSet = new DavPropertyNameSet();
464            final DavProperty<Object> property = new DefaultDavProperty<>(attrName, value, Namespace.EMPTY_NAMESPACE);
465            if (value != null) {
466                properties.add(property);
467            } else {
468                propertyNameSet.add(property.getName()); // remove property
469            }
470
471            final HttpProppatch request = setupRequest(new HttpProppatch(urlStr, properties, propertyNameSet));
472            // AutoClose the underlying HTTP connection which is held by the response object
473            try (CloseableHttpResponse response = (CloseableHttpResponse) executeRequest(request)) {
474                if (!request.succeeded(response)) {
475                    throw new FileSystemException("Property '" + attrName + "' could not be set.");
476                }
477            }
478        } catch (final FileSystemException fse) {
479            throw fse;
480        } catch (final Exception e) {
481            throw new FileSystemException("vfs.provider.webdav/set-attributes", e, getName(), attrName);
482        }
483    }
484
485    private HttpResponse executeRequest(final HttpUriRequest request) throws FileSystemException {
486        final HttpResponse response;
487
488        try {
489            response = executeHttpUriRequest(request);
490            final int status = response.getStatusLine().getStatusCode();
491            if (status == HttpURLConnection.HTTP_NOT_FOUND || status == HttpURLConnection.HTTP_GONE) {
492                EntityUtils.consume(response.getEntity());
493                throw new FileNotFoundException(request.getURI());
494            }
495
496            if (request instanceof BaseDavRequest) {
497                ((BaseDavRequest) request).checkSuccess(response);
498            }
499
500            return response;
501        } catch (final FileSystemException fse) {
502            throw fse;
503        } catch (final IOException e) {
504            throw new FileSystemException(e);
505        } catch (final DavException e) {
506            throw ExceptionConverter.generate(e);
507        }
508    }
509
510    @Override
511    protected FileContentInfoFactory getFileContentInfoFactory() {
512        return new Webdav4FileContentInfoFactory();
513    }
514
515    DavPropertySet getProperties(final GenericURLFileName name) throws FileSystemException {
516        return getProperties(name, DavConstants.PROPFIND_ALL_PROP, new DavPropertyNameSet(), false);
517    }
518
519    DavPropertySet getProperties(final GenericURLFileName name, final DavPropertyNameSet nameSet, final boolean addEncoding)
520            throws FileSystemException {
521        return getProperties(name, DavConstants.PROPFIND_BY_PROPERTY, nameSet, addEncoding);
522    }
523
524    DavPropertySet getProperties(final GenericURLFileName name, final int type, final DavPropertyNameSet nameSet,
525            final boolean addEncoding) throws FileSystemException {
526        try {
527            final String urlStr = toUrlString(name);
528            final HttpPropfind request = setupRequest(new HttpPropfind(urlStr, type, nameSet, DavConstants.DEPTH_0));
529            try (CloseableHttpResponse res = (CloseableHttpResponse) executeRequest(request)) {
530                if (request.succeeded(res)) {
531                    final MultiStatus multiStatus = request.getResponseBodyAsMultiStatus(res);
532                    final MultiStatusResponse response = multiStatus.getResponses()[0];
533                    final DavPropertySet props = response.getProperties(HttpStatus.SC_OK);
534                    if (addEncoding) {
535                        final ContentType resContentType = ContentType.getOrDefault(res.getEntity());
536                        final DavProperty<String> prop = new DefaultDavProperty<>(RESPONSE_CHARSET,
537                                resContentType.getCharset().name());
538                        props.add(prop);
539                    }
540                    return props;
541                }
542                return new DavPropertySet();
543            }
544        } catch (final FileSystemException fse) {
545            throw fse;
546        } catch (final Exception e) {
547            throw new FileSystemException("vfs.provider.webdav/get-property.error", e, getName(), name, type,
548                    nameSet.getContent(), addEncoding);
549        }
550    }
551
552    DavProperty<?> getProperty(final GenericURLFileName fileName, final DavPropertyName name) throws FileSystemException {
553        final DavPropertyNameSet nameSet = new DavPropertyNameSet();
554        nameSet.add(name);
555        final DavPropertySet propertySet = getProperties(fileName, nameSet, false);
556        return propertySet.get(name);
557    }
558
559    DavProperty<?> getProperty(final GenericURLFileName fileName, final String property) throws FileSystemException {
560        return getProperty(fileName, DavPropertyName.create(property));
561    }
562
563    DavPropertySet getPropertyNames(final GenericURLFileName name) throws FileSystemException {
564        return getProperties(name, DavConstants.PROPFIND_PROPERTY_NAMES, new DavPropertyNameSet(), false);
565    }
566
567    /**
568     * Convert the FileName to an encoded url String.
569     *
570     * @param name The FileName.
571     * @return The encoded URL String.
572     */
573    private String hrefString(final GenericURLFileName name) {
574        try {
575            final GenericURLFileName newFile = new GenericURLFileName(getInternalURI().getScheme(), name.getHostName(), name.getPort(), name.getDefaultPort(),
576                    null, null, name.getPath(), name.getType(), name.getQueryString());
577            return newFile.getURIEncoded(getUrlCharset());
578        } catch (final Exception e) {
579            return name.getURI();
580        }
581    }
582
583    private boolean isCurrentFile(final String href, final GenericURLFileName fileName) {
584        String name = hrefString(fileName);
585        if (href.endsWith("/") && !name.endsWith("/")) {
586            name += "/";
587        }
588        return href.equals(name) || href.equals(fileName.getPath());
589    }
590
591    private boolean isDirectory(final GenericURLFileName name) throws IOException {
592        try {
593            final DavProperty<?> property = getProperty(name, DavConstants.PROPERTY_RESOURCETYPE);
594            final Node node;
595            if (property != null && (node = (Node) property.getValue()) != null) {
596                return node.getLocalName().equals(DavConstants.XML_COLLECTION);
597            }
598            return false;
599        } catch (final FileNotFoundException fse) {
600            throw new FileNotFolderException(name);
601        }
602    }
603
604    void log(final Exception ex) {
605        // TODO Consider logging
606    }
607
608    /**
609     * Returns the resource name from the path.
610     *
611     * @param path the path to the file.
612     * @return The resource name
613     */
614    private String resourceName(String path) {
615        if (path.endsWith("/")) {
616            path = path.substring(0, path.length() - 1);
617        }
618        final int i = path.lastIndexOf("/");
619        return i >= 0 ? path.substring(i + 1) : path;
620    }
621
622    private <T extends HttpUriRequest> T setupRequest(final T request) {
623        // NOTE: *FileSystemConfigBuilder takes care of redirect option and user agent.
624        request.addHeader("Cache-control", "no-cache");
625        request.addHeader("Cache-store", "no-store");
626        request.addHeader("Pragma", "no-cache");
627        request.addHeader("Expires", "0");
628        return request;
629    }
630
631    /**
632     * Converts the given URLFileName to an encoded URL String to internally use in real DAV operations.
633     *
634     * @param name The FileName.
635     * @return The encoded URL String.
636     */
637    String toUrlString(final GenericURLFileName name) {
638        return toUrlString(name, true);
639    }
640
641    /**
642     * Converts the given URLFileName to an encoded URL String to internally use in real DAV operations.
643     *
644     * @param name The FileName.
645     * @param includeUserInfo true if user information should be included.
646     * @return The encoded URL String.
647     */
648    private String toUrlString(final GenericURLFileName name, final boolean includeUserInfo) {
649        String user = null;
650        String password = null;
651        if (includeUserInfo) {
652            user = name.getUserName();
653            password = name.getPassword();
654        }
655        try {
656            final GenericURLFileName newFile = new Webdav4FileName(getInternalURI().getScheme(), name.getHostName(), name.getPort(), name.getDefaultPort(),
657                    user, password, name.getPath(), name.getType(), name.getQueryString(),
658                    builder.getAppendTrailingSlash(getFileSystem().getFileSystemOptions()));
659            return newFile.getURIEncoded(getUrlCharset());
660        } catch (final Exception e) {
661            return name.getURI();
662        }
663    }
664}