    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     */
    017    package org.apache.commons.fileupload.util;
    018
    019    import java.io.ByteArrayOutputStream;
    020    import java.io.IOException;
    021    import java.io.InputStream;
    022    import java.io.OutputStream;
    023
    024    import org.apache.commons.fileupload.InvalidFileNameException;
    025
    026
    027    /** Utility class for working with streams.
    028     */
    029    public final class Streams {
    030        /**
    031         * Private constructor, to prevent instantiation.
    032         * This class has only static methods.
    033         */
    034        private Streams() {
    035            // Does nothing
    036        }
    037
    038        /**
    039         * Default buffer size for use in
    040         * {@link #copy(InputStream, OutputStream, boolean)}.
    041         */
    042        private static final int DEFAULT_BUFFER_SIZE = 8192;
    043
    044        /**
    045         * Copies the contents of the given {@link InputStream}
    046         * to the given {@link OutputStream}. Shortcut for
    047         * <pre>
    048         *   copy(pInputStream, pOutputStream, new byte[8192]);
    049         * </pre>
    050         * @param pInputStream The input stream, which is being read.
    051         * It is guaranteed, that {@link InputStream#close()} is called
    052         * on the stream.
    053         * @param pOutputStream The output stream, to which data should
    054         * be written. May be null, in which case the input streams
    055         * contents are simply discarded.
    056         * @param pClose True guarantees, that {@link OutputStream#close()}
    057         * is called on the stream. False indicates, that only
    058         * {@link OutputStream#flush()} should be called finally.
    059         *
    060         * @return Number of bytes, which have been copied.
    061         * @throws IOException An I/O error occurred.
    062         */
    063        public static long copy(InputStream pInputStream,
    064                OutputStream pOutputStream, boolean pClose)
    065                throws IOException {
    066            return copy(pInputStream, pOutputStream, pClose,
    067                    new byte[DEFAULT_BUFFER_SIZE]);
    068        }
    069
    070        /**
    071         * Copies the contents of the given {@link InputStream}
    072         * to the given {@link OutputStream}.
    073         * @param pIn The input stream, which is being read.
    074         *   It is guaranteed, that {@link InputStream#close()} is called
    075         *   on the stream.
    076         * @param pOut The output stream, to which data should
    077         *   be written. May be null, in which case the input streams
    078         *   contents are simply discarded.
    079         * @param pClose True guarantees, that {@link OutputStream#close()}
    080         *   is called on the stream. False indicates, that only
    081         *   {@link OutputStream#flush()} should be called finally.
    082         * @param pBuffer Temporary buffer, which is to be used for
    083         *   copying data.
    084         * @return Number of bytes, which have been copied.
    085         * @throws IOException An I/O error occurred.
    086         */
    087        public static long copy(InputStream pIn,
    088                OutputStream pOut, boolean pClose,
    089                byte[] pBuffer)
    090        throws IOException {
    091            OutputStream out = pOut;
    092            InputStream in = pIn;
    093            try {
    094                long total = 0;
    095                for (;;) {
    096                    int res = in.read(pBuffer);
    097                    if (res == -1) {
    098                        break;
    099                    }
    100                    if (res > 0) {
    101                        total += res;
    102                        if (out != null) {
    103                            out.write(pBuffer, 0, res);
    104                        }
    105                    }
    106                }
    107                if (out != null) {
    108                    if (pClose) {
    109                        out.close();
    110                    } else {
    111                        out.flush();
    112                    }
    113                    out = null;
    114                }
    115                in.close();
    116                in = null;
    117                return total;
    118            } finally {
    119                if (in != null) {
    120                    try {
    121                        in.close();
    122                    } catch (Throwable t) {
    123                        /* Ignore me */
    124                    }
    125                }
    126                if (pClose  &&  out != null) {
    127                    try {
    128                        out.close();
    129                    } catch (Throwable t) {
    130                        /* Ignore me */
    131                    }
    132                }
    133            }
    134        }
    135
    136        /**
    137         * This convenience method allows to read a
    138         * {@link org.apache.commons.fileupload.FileItemStream}'s
    139         * content into a string. The platform's default character encoding
    140         * is used for converting bytes into characters.
    141         * @param pStream The input stream to read.
    142         * @see #asString(InputStream, String)
    143         * @return The streams contents, as a string.
    144         * @throws IOException An I/O error occurred.
    145         */
    146        public static String asString(InputStream pStream) throws IOException {
    147            ByteArrayOutputStream baos = new ByteArrayOutputStream();
    148            copy(pStream, baos, true);
    149            return baos.toString();
    150        }
    151
    152        /**
    153         * This convenience method allows to read a
    154         * {@link org.apache.commons.fileupload.FileItemStream}'s
    155         * content into a string, using the given character encoding.
    156         * @param pStream The input stream to read.
    157         * @param pEncoding The character encoding, typically "UTF-8".
    158         * @see #asString(InputStream)
    159         * @return The streams contents, as a string.
    160         * @throws IOException An I/O error occurred.
    161         */
    162        public static String asString(InputStream pStream, String pEncoding)
    163                throws IOException {
    164            ByteArrayOutputStream baos = new ByteArrayOutputStream();
    165            copy(pStream, baos, true);
    166            return baos.toString(pEncoding);
    167        }
    168
    169        /**
    170         * Checks, whether the given file name is valid in the sense,
    171         * that it doesn't contain any NUL characters. If the file name
    172         * is valid, it will be returned without any modifications. Otherwise,
    173         * an {@link InvalidFileNameException} is raised.
    174         * @param pFileName The file name to check
    175         * @return Unmodified file name, if valid.
    176         * @throws InvalidFileNameException The file name was found to be invalid.
    177         */
    178        public static String checkFileName(String pFileName) {
    179            if (pFileName != null  &&  pFileName.indexOf('\u0000') != -1) {
    180                // pFileName.replace("\u0000", "\\0")
    181                final StringBuffer sb = new StringBuffer();
    182                for (int i = 0;  i < pFileName.length();  i++) {
    183                    char c = pFileName.charAt(i);
    184                    switch (c) {
    185                        case 0:
    186                            sb.append("\\0");
    187                            break;
    188                        default:
    189                            sb.append(c);
    190                            break;
    191                    }
    192                }
    193                throw new InvalidFileNameException(pFileName,
    194                        "Invalid file name: " + sb);
    195            }
    196            return pFileName;
    197        }
    198    }