    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.fileupload2.core;
    018
    019import java.nio.charset.Charset;
    020import java.nio.file.Path;
    021
    022import org.apache.commons.io.FileCleaningTracker;
    023import org.apache.commons.io.build.AbstractOrigin;
    024import org.apache.commons.io.build.AbstractStreamBuilder;
    025import org.apache.commons.io.file.PathUtils;
    026
    027/**
    028 * The default {@link FileItemFactory} implementation.
    029 * <p>
    030 * This implementation creates {@link FileItem} instances which keep their content either in memory, for smaller items, or in a temporary file on disk, for
    031 * larger items. The size threshold, above which content will be stored on disk, is configurable, as is the directory in which temporary files will be created.
    032 * </p>
    033 * <p>
    034 * If not otherwise configured, the default configuration values are as follows:
    035 * </p>
    036 * <ul>
    037 * <li>Size threshold is 10 KB.</li>
    038 * <li>Repository is the system default temporary directory, as returned by {@code System.getProperty("java.io.tmpdir")}.</li>
    039 * </ul>
    040 * <p>
    041 * <b>NOTE</b>: Files are created in the system default temporary directory with predictable names. This means that a local attacker with write access to that
    042 * directory can perform a TOUTOC attack to replace any uploaded file with a file of the attackers choice. The implications of this will depend on how the
    043 * uploaded file is used but could be significant. When using this implementation in an environment with local, untrusted users, {@link Builder#setPath(Path)}
    044 * MUST be used to configure a repository location that is not publicly writable. In a Servlet container the location identified by the ServletContext attribute
    045 * {@code javax.servlet.context.tempdir} may be used.
    046 * </p>
    047 * <p>
    048 * Temporary files, which are created for file items, should be deleted later on. The best way to do this is using a {@link FileCleaningTracker}, which you can
    049 * set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must consider the following: Temporary files are automatically
    050 * deleted as soon as they are no longer needed. (More precisely, when the corresponding instance of {@link java.io.File} is garbage collected.) This is done by
    051 * the so-called reaper thread, which is started and stopped automatically by the {@link FileCleaningTracker} when there are files to be tracked. It might make
    052 * sense to terminate that thread, for example, if your web application ends. See the section on "Resource cleanup" in the users guide of Commons FileUpload.
    053 * </p>
    054 *
    055 * @see Builder
    056 * @see Builder#get()
    057 */
    058public final class DiskFileItemFactory implements FileItemFactory<DiskFileItem> {
    059
    060    /**
    061     * Builds a new {@link DiskFileItemFactory} instance.
    062     * <p>
    063     * For example:
    064     * </p>
    065     *
    066     * <pre>{@code
    067     * DiskFileItemFactory factory = DiskFileItemFactory.builder().setPath(path).setBufferSize(DEFAULT_THRESHOLD).get();
    068     * }
    069     * </pre>
    070     */
    071    public static class Builder extends AbstractStreamBuilder<DiskFileItemFactory, Builder> {
    072
    073        /**
    074         * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
    075         * <p>
    076         * May be null, if tracking files is not required.
    077         * </p>
    078         */
    079        private FileCleaningTracker fileCleaningTracker;
    080
    081        public Builder() {
    082            setBufferSize(DEFAULT_THRESHOLD);
    083            setPath(PathUtils.getTempDirectory());
    084            setCharset(DiskFileItem.DEFAULT_CHARSET);
    085            setCharsetDefault(DiskFileItem.DEFAULT_CHARSET);
    086        }
    087
    088        /**
    089         * Constructs a new instance.
    090         * <p>
    091         * This builder use the aspects Path and buffer size.
    092         * </p>
    093         * <p>
    094         * You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an
    095         * {@link UnsupportedOperationException}.
    096         * </p>
    097         *
    098         * @return a new instance.
    099         * @throws UnsupportedOperationException if the origin cannot provide a Path.
    100         * @see AbstractOrigin#getReader(Charset)
    101         */
    102        @Override
    103        public DiskFileItemFactory get() {
    104            return new DiskFileItemFactory(getPath(), getBufferSize(), getCharset(), fileCleaningTracker);
    105        }
    106
    107        /**
    108         * Sets the tracker, which is responsible for deleting temporary files.
    109         *
    110         * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking.
    111         * @return this
    112         */
    113        public Builder setFileCleaningTracker(final FileCleaningTracker fileCleaningTracker) {
    114            this.fileCleaningTracker = fileCleaningTracker;
    115            return this;
    116        }
    117
    118    }
    119
    120    /**
    121     * The default threshold in bytes above which uploads will be stored on disk.
    122     */
    123    public static final int DEFAULT_THRESHOLD = 10_240;
    124
    125    /**
    126     * Constructs a new {@link Builder}.
    127     *
    128     * @return a new {@link Builder}.
    129     */
    130    public static Builder builder() {
    131        return new Builder();
    132    }
    133
    134    /**
    135     * The directory in which uploaded files will be stored, if stored on disk.
    136     */
    137    private final Path repository;
    138
    139    /**
    140     * The threshold above which uploads will be stored on disk.
    141     */
    142    private final int threshold;
    143
    144    /**
    145     * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
    146     * <p>
    147     * May be null, if tracking files is not required.
    148     * </p>
    149     */
    150    private final FileCleaningTracker fileCleaningTracker;
    151
    152    /**
    153     * Default content Charset to be used when no explicit Charset parameter is provided by the sender.
    154     */
    155    private final Charset charsetDefault;
    156
    157    /**
    158     * Constructs a preconfigured instance of this class.
    159     *
    160     * @param repository          The data repository, which is the directory in which files will be created, should the item size exceed the threshold.
    161     * @param threshold           The threshold, in bytes, below which items will be retained in memory and above which they will be stored as a file.
    162     * @param charsetDefault      Sets the default charset for use when no explicit charset parameter is provided by the sender.
    163     * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking.
    164     */
    165    private DiskFileItemFactory(final Path repository, final int threshold, final Charset charsetDefault, final FileCleaningTracker fileCleaningTracker) {
    166        this.threshold = threshold;
    167        this.repository = repository;
    168        this.charsetDefault = charsetDefault;
    169        this.fileCleaningTracker = fileCleaningTracker;
    170    }
    171
    172    @SuppressWarnings("unchecked")
    173    @Override
    174    public DiskFileItem.Builder fileItemBuilder() {
    175        // @formatter:off
    176        return DiskFileItem.builder()
    177                .setBufferSize(threshold)
    178                .setCharset(charsetDefault)
    179                .setFileCleaningTracker(fileCleaningTracker)
    180                .setPath(repository);
    181        // @formatter:on
    182    }
    183
    184    /**
    185     * Gets the default charset for use when no explicit charset parameter is provided by the sender.
    186     *
    187     * @return the default charset
    188     */
    189    public Charset getCharsetDefault() {
    190        return charsetDefault;
    191    }
    192
    193    /**
    194     * Gets the tracker, which is responsible for deleting temporary files.
    195     *
    196     * @return An instance of {@link FileCleaningTracker}, or null (default), if temporary files aren't tracked.
    197     */
    198    public FileCleaningTracker getFileCleaningTracker() {
    199        return fileCleaningTracker;
    200    }
    201
    202    /**
    203     * Gets the directory used to temporarily store files that are larger than the configured size threshold.
    204     *
    205     * @return The directory in which temporary files will be located.
    206     */
    207    public Path getRepository() {
    208        return repository;
    209    }
    210
    211    /**
    212     * Gets the size threshold beyond which files are written directly to disk. The default value is {@value #DEFAULT_THRESHOLD} bytes.
    213     *
    214     * @return The size threshold in bytes.
    215     */
    216    public int getThreshold() {
    217        return threshold;
    218    }
    219}