Package org.apache.cocoon.servlet.multipart

Source Code of org.apache.cocoon.servlet.multipart.MultipartParser

/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.servlet.multipart;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;

import org.apache.cocoon.util.NullOutputStream;

/**
* This class is used to implement a multipart request wrapper.
* It will parse the http post stream and and fill it's hashtable with values.
*
* The hashtable will contain:
* Vector: inline part values
* FilePart: file part
*
* @author <a href="mailto:j.tervoorde@home.nl">Jeroen ter Voorde</a>
* @version CVS $Id: MultipartParser.java 280876 2005-09-14 15:44:29Z sylvain $
*/
public class MultipartParser {

    private final static int FILE_BUFFER_SIZE = 4096;

    private static final int MAX_BOUNDARY_SIZE = 128;

    private boolean saveUploadedFilesToDisk;

    private File uploadDirectory = null;

    private boolean allowOverwrite;

    private boolean silentlyRename;
   
    private int maxUploadSize;

    private String characterEncoding;
   
    private Hashtable parts;
   
    private boolean oversized = false;
   
    private int contentLength;
   
    /**
     * Constructor, parses given request
     *
     * @param saveUploadedFilesToDisk Write fileparts to the uploadDirectory. If true the corresponding object
     *              in the hashtable will contain a FilePartFile, if false a FilePartArray
     * @param uploadDirectory The directory to write to if saveUploadedFilesToDisk is true.
     * @param allowOverwrite Allow existing files to be overwritten.
     * @param silentlyRename If file exists rename file (using filename+number).
     * @param maxUploadSize The maximum content length accepted.
     * @param characterEncoding The character encoding to be used.
     */
    public MultipartParser(boolean saveUploadedFilesToDisk,
                           File uploadDirectory,
                           boolean allowOverwrite,
                           boolean silentlyRename,
                           int maxUploadSize,
                           String characterEncoding)
    {
        this.saveUploadedFilesToDisk = saveUploadedFilesToDisk;
        this.uploadDirectory = uploadDirectory;
        this.allowOverwrite = allowOverwrite;
        this.silentlyRename = silentlyRename;
        this.maxUploadSize = maxUploadSize;
        this.characterEncoding = characterEncoding;
    }

    private void parseParts(int contentLength, String contentType, InputStream requestStream)
    throws IOException, MultipartException {
        this.contentLength = contentLength;
        if (contentLength > this.maxUploadSize) {
            this.oversized = true;
        }

        BufferedInputStream bufferedStream = new BufferedInputStream(requestStream);
        PushbackInputStream pushbackStream = new PushbackInputStream(bufferedStream, MAX_BOUNDARY_SIZE);
        TokenStream stream = new TokenStream(pushbackStream);

        parseMultiPart(stream, getBoundary(contentType));

    }
   
    public Hashtable getParts(int contentLength, String contentType, InputStream requestStream)
    throws IOException, MultipartException {
        this.parts = new Hashtable();
        parseParts(contentLength, contentType, requestStream);
        return this.parts;
    }
   
    public Hashtable getParts(HttpServletRequest request) throws IOException, MultipartException {
        this.parts = new Hashtable();
       
        // Copy all parameters coming from the request URI to the parts table.
        // This happens when a form's action attribute has some parameters
        Enumeration names = request.getParameterNames();
        while(names.hasMoreElements()) {
            String name = (String)names.nextElement();
            String[] values = request.getParameterValues(name);
            Vector v = new Vector(values.length);
            for (int i = 0; i < values.length; i++) {
                v.add(values[i]);
            }
            this.parts.put(name, v);
        }
        parseParts(request.getContentLength(), request.getContentType(), request.getInputStream());   
        return this.parts;   
    }
   
    /**
     * Parse a multipart block
     *
     * @param ts
     * @param boundary
     *
     * @throws IOException
     * @throws MultipartException
     */
    private void parseMultiPart(TokenStream ts, String boundary)
            throws IOException, MultipartException {

        ts.setBoundary(boundary.getBytes());
        ts.read();    // read first boundary away
        ts.setBoundary(("\r\n" + boundary).getBytes());

        while (ts.getState() == TokenStream.STATE_NEXTPART) {
            ts.nextPart();
            parsePart(ts);
        }

        if (ts.getState() != TokenStream.STATE_ENDMULTIPART) {    // sanity check
            throw new MultipartException("Malformed stream");
        }
    }

    /**
     * Parse a single part
     *
     * @param ts
     *
     * @throws IOException
     * @throws MultipartException
     */
    private void parsePart(TokenStream ts)
            throws IOException, MultipartException {

        Hashtable headers = new Hashtable();
        headers = readHeaders(ts);
        try {
            if (headers.containsKey("filename")) {
            if (!"".equals(headers.get("filename"))) {
                  parseFilePart(ts, headers);
            } else {
              // IE6 sends an empty part with filename="" for
              // empty upload fields. Just parse away the part
              byte[] buf = new byte[32];
              while(ts.getState() == TokenStream.STATE_READING)
                ts.read(buf)
            }
            } else if (((String) headers.get("content-disposition"))
                    .toLowerCase().equals("form-data")) {
                parseInlinePart(ts, headers);
            }

            // FIXME: multipart/mixed parts are untested.
            else if (((String) headers.get("content-disposition")).toLowerCase()
                    .indexOf("multipart") > -1) {
                parseMultiPart(new TokenStream(ts, MAX_BOUNDARY_SIZE),
                        "--" + (String) headers.get("boundary"));
                ts.read();    // read past boundary
            } else {
                throw new MultipartException("Unknown part type");
            }
        } catch (IOException e) {
            throw new MultipartException("Malformed stream: " + e.getMessage());
        } catch (NullPointerException e) {
            e.printStackTrace();
            throw new MultipartException("Malformed header");
        }
    }

    /**
     * Parse a file part
     *
     * @param in
     * @param headers
     *
     * @throws IOException
     * @throws MultipartException
     */
    private void parseFilePart(TokenStream in, Hashtable headers)
            throws IOException, MultipartException {

        byte[] buf = new byte[FILE_BUFFER_SIZE];
        OutputStream out;
        File file = null;

        if (oversized) {
            out = new NullOutputStream();
        } else if (!saveUploadedFilesToDisk) {
            out = new ByteArrayOutputStream();
        } else {
            String fileName = (String) headers.get("filename");
            if(File.separatorChar == '\\')
                fileName = fileName.replace('/','\\');
            else
                fileName = fileName.replace('\\','/');

            String filePath = uploadDirectory.getPath() + File.separator;
            fileName = new File(fileName).getName();
            file = new File(filePath + fileName);

            if (!allowOverwrite && !file.createNewFile()) {
                if (silentlyRename) {
                    int c = 0;
                    do {
                        file = new File(filePath + c++ + "_" + fileName);
                    } while (!file.createNewFile());
                } else {
                    throw new MultipartException("Duplicate file '" + file.getName()
                        + "' in '" + file.getParent() + "'");
                }
            }

            out = new FileOutputStream(file);
        }

        int length = 0; // Track length for OversizedPart
        try {
            int read = 0;
            while (in.getState() == TokenStream.STATE_READING) {    // read data
                read = in.read(buf);
                length += read;
                out.write(buf, 0, read);
            }
        } catch (IOException ioe) {
            // don't let incomplete file uploads pile up in the upload dir.
            // this usually happens with aborted form submits containing very large files.
            out.close();
            out = null;
            if ( file!=null ) file.delete();
            throw ioe;
        } finally {
            if ( out!=null ) out.close();
        }
       
        String name = (String)headers.get("name");
        if (oversized) {
            this.parts.put(name, new RejectedPart(headers, length, this.contentLength, this.maxUploadSize));
        } else if (file == null) {
            byte[] bytes = ((ByteArrayOutputStream) out).toByteArray();
            this.parts.put(name, new PartInMemory(headers, new ByteArrayInputStream(bytes), bytes.length));
        } else {
            this.parts.put(name, new PartOnDisk(headers, file));
        }
    }

    /**
     * Parse an inline part
     *
     * @param in
     * @param headers
     *
     * @throws IOException
     */
    private void parseInlinePart(TokenStream in, Hashtable headers)
            throws IOException {

    // Buffer incoming bytes for proper string decoding (there can be multibyte chars)
        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        while (in.getState() == TokenStream.STATE_READING) {
          int c = in.read();
          if (c != -1) bos.write(c);
        }
       
        String field = (String) headers.get("name");
        Vector v = (Vector) this.parts.get(field);

        if (v == null) {
            v = new Vector();
            this.parts.put(field, v);
        }

        v.add(new String(bos.toByteArray(), this.characterEncoding));
    }

    /**
     * Read part headers
     *
     * @param in
     *
     * @return
     *
     * @throws IOException
     */
    private Hashtable readHeaders(TokenStream in) throws IOException {

        Hashtable headers = new Hashtable();
        String hdrline = readln(in);

        while (!"".equals(hdrline)) {
            StringTokenizer tokenizer = new StringTokenizer(hdrline);

            headers.put(tokenizer.nextToken(" :").toLowerCase(),
                    tokenizer.nextToken(" :;"));

          // The extra tokenizer.hasMoreTokens() in headers.put
          // handles the filename="" case IE6 submits for an empty
          // upload field.
            while (tokenizer.hasMoreTokens()) {
                headers.put(tokenizer.nextToken(" ;=\""),
                        tokenizer.hasMoreTokens()?tokenizer.nextToken("=\""):"");
            }

            hdrline = readln(in);
        }

        return headers;
    }

    /**
     * Get boundary from contentheader
     *
     * @param hdr
     *
     * @return
     */
    private String getBoundary(String hdr) {

        int start = hdr.toLowerCase().indexOf("boundary=");
        if (start > -1) {
            return "--" + hdr.substring(start + 9);
        } else {
            return null;
        }
    }

    /**
     * Read string until newline or end of stream
     *
     * @param in
     *
     * @return
     *
     * @throws IOException
     */
    private String readln(TokenStream in) throws IOException {
     
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
     
        int b = in.read();

        while ((b != -1) && (b != '\r')) {
            bos.write(b);
            b = in.read();
        }

        if (b == '\r') {
            in.read();    // read '\n'
        }

        return new String(bos.toByteArray(), this.characterEncoding);
    }
}
TOP

Related Classes of org.apache.cocoon.servlet.multipart.MultipartParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.