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    }