<commit_before>/*
 * SubmitServlet.java
 *
 * Version: $Revision$
 *
 * Date: $Date$
 *
 * Copyright (c) 2001, Hewlett-Packard Company and Massachusetts
 * Institute of Technology.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the Hewlett-Packard Company nor the name of the
 * Massachusetts Institute of Technology nor the names of their
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
package org.dspace.app.webui.servlet;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import org.dspace.app.webui.util.Authenticate;
import org.dspace.app.webui.util.FileUploadRequest;
import org.dspace.app.webui.util.JSPManager;
import org.dspace.app.webui.util.SubmissionInfo;
import org.dspace.app.webui.util.UIUtil;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DCDate;
import org.dspace.content.DCPersonName;
import org.dspace.content.DCSeriesNumber;
import org.dspace.content.DCValue;
import org.dspace.content.FormatIdentifier;
import org.dspace.content.InProgressSubmission;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.eperson.EPerson;
import org.dspace.workflow.WorkflowItem;
import org.dspace.workflow.WorkflowManager;


/**
 * Submission servlet for DSpace.  Handles the initial submission of items, as
 * well as the editing of items further down the line.
 * <P>
 * Whenever the submit servlet receives a GET request, this is taken to
 * indicate the start of a fresh new submission, where no collection has been
 * selected, and the submission process is started from scratch.
 * <P>
 * All other interactions happen via POSTs.  Part of the post will normally be
 * a (hidden) "step" parameter, which will correspond to the form that the
 * user has just filled out.  If this is absent, step 0 (select collection)
 * is assumed, meaning that it's simple to place "Submit to this collection"
 * buttons on collection home pages.
 * <P>
 * According to the step number of the incoming form, the values posted from
 * the form are processed (using the process* methods), and the item updated
 * as appropriate.  The servlet then forwards control of the request to the
 * appropriate JSP (from jsp/submit) to render the next stage
 * of the process or an error if appropriate.  Each of these JSPs may
 * require that attributes be passed in.  Check the comments at the top of a
 * JSP to see which attributes are needed.  All submit-related forms require a
 * properly initialised SubmissionInfo object to be present in the the
 * "submission.info" attribute.  This holds the core information relevant to
 * the submission, e.g. the item, personal workspace or workflow item, the
 * submitting "e-person", and the target collection.
 * <P>
 * When control of the request reaches a JSP, it is assumed that all checks,
 * interactions with the database and so on have been performed and that all
 * necessary information to render the form is in memory.  e.g. The
 * SubmitFormInfo object passed in must be correctly filled out.  Thus the
 * JSPs do no error or integrity checking; it is the servlet's responsibility
 * to ensure that everything is prepared.  The servlet is fairly diligent
 * about ensuring integrity at each step.
 * <P>
 * Each step has an integer constant defined below.  The main sequence of the
 * submission procedure always runs from 0 upwards, until SUBMISSION_COMPLETE.
 * Other, not-in-sequence steps (such as the cancellation screen and the
 * "previous version ID verification" screen) have numbers much higher than
 * SUBMISSION_COMPLETE.  These conventions allow the progress bar component
 * of the submission forms to render the user's progress through the process.
 *
 * @author  <NAME>
 * @version $Revision$
 */
public class SubmitServlet extends DSpaceServlet
{
    // Steps in the submission process

    /** Selection collection step */
    public static final int SELECT_COLLECTION = 0;

    /** Ask initial questions about the submission step */
    public static final int INITIAL_QUESTIONS = 1;

    /** Edit DC metadata page 1 step */
    public static final int EDIT_METADATA_1 = 2;

    /** Edit DC metadata page 2 step */
    public static final int EDIT_METADATA_2 = 3;

    /**
     * Upload files step.  Note this doesn't correspond to an actual
     * page, since the upload file step consists of a number of
     * pages with no definite order.  This is just used for the progress
     * bar.
     */
    public static final int UPLOAD_FILES = 4;

    /** Review submission step */
    public static final int REVIEW_SUBMISSION = 5;

    /** Grant license step */
    public static final int GRANT_LICENSE = 6;

    /** Submission completed step */
    public static final int SUBMISSION_COMPLETE = 7;

    // Steps which aren't part of the main sequence, but rather
    // short "diversions" are given high step numbers.  The main sequence
    // is defined as being steps 0 to SUBMISSION_COMPLETE.

    /** Cancellation of a submission */
    public static final int SUBMISSION_CANCELLED = 101;

    /** List of uploaded files */
    public static final int FILE_LIST = 102;

    /** Choose file page */
    public static final int CHOOSE_FILE = 103;

    /** File format page */
    public static final int GET_FILE_FORMAT = 104;

    /** Error uploading file */
    public static final int UPLOAD_ERROR = 105;

    /** Change file description page */
    public static final int CHANGE_FILE_DESCRIPTION = 106;

    /**
     * Verify pruning of extra files, titles, dates as a result of
     * changing an answer to one of the initial questions
     */
    public static final int VERIFY_PRUNE = 107;

    /** log4j logger */
    private static Logger log = Logger.getLogger(SubmitServlet.class);


    protected void doDSGet(Context context,
        HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        /*
         * Possible GET parameters:
         *
         *   resume=<workspace_item_id>
         *         - Resumes submitting the given workspace item
         *
         *   workflow=<workflow_id>
         *         - Starts editing the given workflow item in workflow mode
         *
         * With no parameters, A GET starts a new submission.  What happens
         * depends on the context of the user (where they are.)  If they're not
         * in a community or collection, they can choose any collection
         * from the list of all collections.  If they're in a community,
         * the list of collections they can choose from will be limited to
         * those within the current community.  If the user has selected
         * a collection, a new submission will be started in that collection.
         */

        String workspaceID = request.getParameter("resume");
        if (workspaceID != null)
        {
            try
            {
                WorkspaceItem wi = WorkspaceItem.find(context,
                    Integer.parseInt(workspaceID));

                SubmissionInfo si = new SubmissionInfo();
                si.submission = wi;
                doStep(context, request, response, si, INITIAL_QUESTIONS);
            }
            catch (NumberFormatException nfe)
            {
                log.warn(LogManager.getHeader(context,
                    "bad_workspace_id",
                    "bad_id=" + workspaceID));
                JSPManager.showInvalidIDError(request,
                    response,
                    workspaceID,
                    -1);
            }
            return;
        }

        String workflowID = request.getParameter("workflow");
        if (workflowID != null)
        {
            try
            {
                WorkflowItem wi = WorkflowItem.find(context,
                    Integer.parseInt(workflowID));

                SubmissionInfo si = new SubmissionInfo();
                si.submission = wi;
                doStep(context, request, response, si, INITIAL_QUESTIONS);
            }
            catch (NumberFormatException nfe)
            {
                log.warn(LogManager.getHeader(context,
                    "bad_workflow_id",
                    "bad_id=" + workflowID));
                JSPManager.showInvalidIDError(request,
                    response,
                    workflowID,
                    -1);
            }
            return;
        }



        Community  com = UIUtil.getCommunityLocation (request);
        Collection col = UIUtil.getCollectionLocation(request);

        if (col != null)
        {
            // In a collection, skip the "choose selection" stage
            // Create a workspace item
            WorkspaceItem wi = WorkspaceItem.create(context, col, true);

            // Proceed to first step
            SubmissionInfo si = new SubmissionInfo();
            si.submission = wi;
            doStep(context, request, response, si, INITIAL_QUESTIONS);
            context.complete();
        }
        else
        {
            Collection[] collections;

            if (com != null)
            {
                // In a community.  Show collections in that community only.
                collections = com.getCollections();
            }
            else
            {
                // Show all collections
                collections = Collection.findAll(context);
            }

            log.info(LogManager.getHeader(context,
                "select_collection",
                ""));

            request.setAttribute("collections", collections);
            JSPManager.showJSP(request, response,
                "/submit/select-collection.jsp");
        }
    }


    protected void doDSPost(Context context,
        HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        // First of all, we need to work out if this is a multipart request
        // The file upload page uses those
        String contentType = request.getContentType();

        if (contentType != null &&
            contentType.indexOf("multipart/form-data") != -1)
        {
            // This is a multipart request, so it's a file upload
            processChooseFile(context, request, response);
            return;
        }

        // First get the step
        int step = UIUtil.getIntParameter(request, "step");

        // select collection is a special case - no submissioninfo object
        // If no step was given, we also assume "select collection"
        // (Submit button on collection home page or elsewhere)
        if (step == SELECT_COLLECTION || step == -1)
        {
            processSelectCollection(context, request, response);
            return;
        }

        // Get submission info
        SubmissionInfo subInfo = getSubmissionInfo(context, request);

        if (subInfo == null)
        {
            /*
             * Work around for problem where people select "is a thesis",
             * see the error page, and then use their "back" button thinking
             * they can start another submission - it's been removed so the ID
             * in the form is invalid.  If we detect the "removed_thesis"
             * attribute we display a friendly message instead of an integrity
             * error.
             */
            if (request.getSession().getAttribute("removed_thesis") != null)
            {
                request.getSession().removeAttribute("removed_thesis");
                JSPManager.showJSP(request,
                    response,
                    "/submit/thesis-removed-workaround.jsp");
                return;
            }
            else
            {
                /*
                 * If the submission info was invalid, set the step to an
                 * invalid number so an integrity error will be shown
                 */
                step = -1;
            }
        }


        switch (step)
        {
        case INITIAL_QUESTIONS:
            processInitialQuestions(context, request, response, subInfo);
            break;

        case EDIT_METADATA_1:
            processEditMetadataOne(context, request, response, subInfo);
            break;

        case EDIT_METADATA_2:
            processEditMetadataTwo(context, request, response, subInfo);
            break;

        case GET_FILE_FORMAT:
            processGetFileFormat(context, request, response, subInfo);
            break;

        case FILE_LIST:
            processFileList(context, request, response, subInfo);
            break;

        case UPLOAD_ERROR:
            processUploadError(context, request, response, subInfo);
            break;

        case CHANGE_FILE_DESCRIPTION:
            processChangeFileDescription(context, request, response, subInfo);
            break;

        case REVIEW_SUBMISSION:
            processReview(context, request, response, subInfo);
            break;

        case GRANT_LICENSE:
            processLicense(context, request, response, subInfo);
            break;

        case SUBMISSION_CANCELLED:
            processCancellation(context, request, response, subInfo);
            break;

        case VERIFY_PRUNE:
            processVerifyPrune(context, request, response, subInfo);
            break;

        default:
            log.warn(LogManager.getHeader(context,
                "integrity_error",
                UIUtil.getRequestLogInfo(request)));
            JSPManager.showIntegrityError(request, response);
        }
    }


    //****************************************************************
    //****************************************************************
    //             METHODS FOR PROCESSING POSTED FORMS
    //****************************************************************
    //****************************************************************

    /**
     * Process the selection collection stage, or the clicking of a
     * "submit to this collection" button on a collection home page.
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     */
    private void processSelectCollection(Context context,
        HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        // The user might have clicked cancel.  We don't do a
        // standard cancellation at this stage, since we don't
        // actually have an item to keep or remove yet.
        if (request.getParameter("submit_cancel") != null)
        {
            // Just send them to their "My DSpace" for now.
            response.sendRedirect(response.encodeRedirectURL(
                    request.getContextPath() + "/mydspace"));
            return;
        }

        // First we find the collection
        int id = UIUtil.getIntParameter(request, "collection");
        Collection col = Collection.find(context, id);

        // Show an error if we don't have a collection
        if (col == null)
        {
            JSPManager.showInvalidIDError(request,
                response,
                request.getParameter("collection"),
                Constants.COLLECTION);
        }
        else
        {
            WorkspaceItem wi = WorkspaceItem.create(context, col, true);

            // Proceed to first step
            SubmissionInfo si = new SubmissionInfo();
            si.submission = wi;
            doStep(context, request, response, si, INITIAL_QUESTIONS);

            context.complete();
        }
    }


    /**
     * process input from initial-questions.jsp
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processInitialQuestions(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        String buttonPressed = UIUtil.getSubmitButton(request, "submit_next");

        // Firstly, check for a click of the cancel button.
        if (buttonPressed.equals("submit_cancel"))
        {
            doCancellation(request,
                response,
                subInfo,
                INITIAL_QUESTIONS,
                INITIAL_QUESTIONS);
            return;
        }

        // Get the values from the form
        boolean multipleTitles =
            UIUtil.getBoolParameter(request, "multiple_titles");
        boolean publishedBefore =
            UIUtil.getBoolParameter(request, "published_before");
        boolean multipleFiles =
            UIUtil.getBoolParameter(request, "multiple_files");
        boolean isThesis =
            UIUtil.getBoolParameter(request, "is_thesis");

        if (isWorkflow(subInfo))
        {
            // Thesis question does not appear in workflow mode..
            isThesis = false;
            // Pretend "multiple files" is true in workflow mode
            // (There will always be the license file)
            multipleFiles = true;
        }

        // First and foremost - if it's a thesis, reject the submission
        // FIXME: MIT-SPECIFIC
        if (isThesis)
        {
            WorkspaceItem wi = (WorkspaceItem) subInfo.submission;
            wi.deleteAll();

            // Remember that we've removed a thesis in the session
            request.getSession().setAttribute(
                "removed_thesis",
                new Boolean(true));

            // Display appropriate message
            JSPManager.showJSP(request, response, "/submit/no-theses.jsp");

            context.complete();
            return;
        }

        // Now check to see if the changes will remove any values
        // (i.e. multiple files, titles or an issue date.)
        boolean willRemoveTitles = false;
        boolean willRemoveDate = false;
        boolean willRemoveFiles = false;

        if (multipleTitles == false)
        {
            DCValue[] altTitles = subInfo.submission.getItem().getDC(
                "title", "alternative", Item.ANY);

            willRemoveTitles = altTitles.length > 0;
        }

        if (publishedBefore == false)
        {
            DCValue[] dateIssued = subInfo.submission.getItem().getDC(
                "date", "issued", Item.ANY);

            willRemoveDate = dateIssued.length > 0;
        }

        if (multipleFiles == false)
        {
            // FIXME: Assuming each bundle has but one bitstream in it
            Bundle[] bundles = subInfo.submission.getItem().getBundles();

            willRemoveFiles = bundles.length > 1;
        }

        // If anything is going to be removed from the item as a result
        // of changing the answer to one of the questions, we need
        // to inform the user and make sure that's OK
        if (willRemoveTitles || willRemoveDate || willRemoveFiles)
        {
            // Verify pruning of extra bits
            request.setAttribute("submission.info", subInfo);
            request.setAttribute(
                "multiple.titles", new Boolean(multipleTitles));
            request.setAttribute(
                "published.before", new Boolean(publishedBefore));
            request.setAttribute(
                "multiple.files", new Boolean(multipleFiles));
            request.setAttribute(
                "will.remove.titles", new Boolean(willRemoveTitles));
            request.setAttribute(
                "will.remove.date", new Boolean(willRemoveDate));
            request.setAttribute(
                "will.remove.files", new Boolean(willRemoveFiles));
            request.setAttribute("button.pressed",
                UIUtil.getSubmitButton(request, "submit_next"));

            JSPManager.showJSP(request, response, "/submit/verify-prune.jsp");
        }
        else
        {
            // Nothing needs removing, so just make the changes
            subInfo.submission.setMultipleTitles(multipleTitles);
            subInfo.submission.setPublishedBefore(publishedBefore);

            // "Multiple files" irrelevant in workflow mode
            if (!isWorkflow(subInfo))
            {
                subInfo.submission.setMultipleFiles(multipleFiles);
            }

            subInfo.submission.update();

            // On to the next stage
            if (buttonPressed.equals("submit_next"))
            {
                // Update user's progress
                userHasReached(subInfo, EDIT_METADATA_1);

                // User has clicked "Next"
                doStep(context, request, response, subInfo, EDIT_METADATA_1);
            }
            else
            {
                // Progress bar button clicked
                doStepJump(context, request, response, subInfo);
            }

            context.complete();
        }
    }


    /**
     * Process input from "verify prune" step
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processVerifyPrune(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        if (request.getParameter("do_not_proceed") != null)
        {
            // User cancelled
            doStep(context, request, response, subInfo, INITIAL_QUESTIONS);
            return;
        }

        // User elected to proceed - do the pruning
        // Get the values from the form
        boolean multipleTitles =
            UIUtil.getBoolParameter(request, "multiple_titles");
        boolean publishedBefore =
            UIUtil.getBoolParameter(request, "published_before");

        // Multiple files question does not appear in workflow mode.
        // Since the submission will have a license, the answer to
        // this question will always be "yes"
        boolean multipleFiles = (isWorkflow(subInfo) ||
            UIUtil.getBoolParameter(request, "multiple_files"));

        if (!multipleTitles)
        {
            subInfo.submission.getItem().clearDC("title",
                "alternative", Item.ANY);
        }

        if (publishedBefore == false)
        {
            subInfo.submission.getItem().clearDC("date", "issued", Item.ANY);
        }

        if (multipleFiles == false)
        {
            // FIXME: Assuming each bundle has but one bitstream in it
            Item item = subInfo.submission.getItem();
            Bundle[] bundles = item.getBundles();

            // Remove all but the first bundle
            for (int i = 1; i < bundles.length; i++)
            {
                item.removeBundle(bundles[i]);
            }
        }

        // Nothing needs removing, so just make the changes
        subInfo.submission.setMultipleTitles(multipleTitles);
        subInfo.submission.setPublishedBefore(publishedBefore);

        // "Multiple files" irrelevant in workflow mode
        if (!isWorkflow(subInfo))
        {
            subInfo.submission.setMultipleFiles(multipleFiles);
        }

        subInfo.submission.update();

        // Everything went OK if we get to here, so now response
        // to the original button press
        if (request.getParameter("submit_next") != null)
        {
            // Update user's progress
            userHasReached(subInfo, EDIT_METADATA_1);

            // User has clicked "Next"
            doStep(context, request, response, subInfo, EDIT_METADATA_1);

            context.complete();
        }
        else
        {
            // Progress bar button clicked
            doStepJump(context, request, response, subInfo);

            context.complete();
        }
    }


    /**
     * process input from edit-metadata-1.jsp
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processEditMetadataOne(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        String buttonPressed = UIUtil.getSubmitButton(request, "submit_next");

        // Firstly, check for a click of the cancel button.
        if (buttonPressed.equals("submit_cancel"))
        {
            doCancellation(request,
                response,
                subInfo,
                EDIT_METADATA_1,
                EDIT_METADATA_1);
            return;
        }

        Item item = subInfo.submission.getItem();

        // Update the item metadata.
        readNames(request, item, "contributor", "author", true);
        readText(request, item, "title", null, false, "en");

        if (subInfo.submission.hasMultipleTitles())
        {
            readText(request, item, "title", "alternative", true, "en");
        }

        if (subInfo.submission.isPublishedBefore())
        {
            readDate(request, item, "date", "issued");
        }

        readSeriesNumbers(request, item, "relation", "ispartofseries", true);

        // FIXME: Maybe should do integrity check using language object
        readText(request, item, "language", "iso", false, null);

        // Identifiers - A special case so do this here
        // First, remove existing identifiers.
        subInfo.submission.getItem().clearDC("identifier", Item.ANY, Item.ANY);

        // Get qualifiers and values
        List quals = getRepeatedParameter(request, "identifier_qualifier");
        List vals = getRepeatedParameter(request, "identifier_value");

        // Add to item DC
        for (int i = 0; i<vals.size(); i++)
        {
            String thisQual = (String) quals.get(i);
            String thisVal = (String) vals.get(i);

            if (!buttonPressed.equals("submit_identifier_remove_" + i) &&
                !thisVal.equals(""))
            {
                item.addDC("identifier", thisQual, null, thisVal);
            }
        }

        int nextStep = -1;

        // Proceed according to button pressed
        if (buttonPressed.equals("submit_contributor_author_more"))
        {
            // "Add more" clicked for authors
            subInfo.moreBoxesFor = "contributor_author";
            subInfo.jumpToField = "contributor_author";
            nextStep = EDIT_METADATA_1;
        }
        else if (subInfo.submission.hasMultipleTitles() &&
            buttonPressed.equals("submit_title_alternative_more"))
        {
            // "Add more" clicked for alternative titles
            subInfo.moreBoxesFor = "title_alternative";
            subInfo.jumpToField = "title_alternative";
            nextStep = EDIT_METADATA_1;
        }
        else if (buttonPressed.equals("submit_relation_ispartofseries_more"))
        {
            // "Add more" clicked for series/number
            subInfo.moreBoxesFor = "relation_ispartofseries";
            subInfo.jumpToField = "relation_ispartofseries";
            nextStep = EDIT_METADATA_1;
        }
        else if (buttonPressed.equals("submit_identifier_more"))
        {
            // "Add more" clicked for identifier
            subInfo.moreBoxesFor = "identifier";
            subInfo.jumpToField = "identifier";
            nextStep = EDIT_METADATA_1;
        }
        else if (buttonPressed.equals("submit_prev"))
        {
            nextStep = INITIAL_QUESTIONS;
        }
        else if (buttonPressed.equals("submit_next"))
        {
            userHasReached(subInfo, EDIT_METADATA_2);
            nextStep = EDIT_METADATA_2;
        }
        else if (buttonPressed.indexOf("remove") > -1)
        {
            // Remove button pressed - stay with same form
            nextStep = EDIT_METADATA_1;
        }

        // Write changes to database
        subInfo.submission.update();

        if (nextStep != -1)
        {
            doStep(context, request, response, subInfo, nextStep);
        }
        else
        {
            doStepJump(context, request, response, subInfo);
        }

        context.complete();
    }


    /**
     * process input from edit-metadata-2.jsp
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processEditMetadataTwo(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        String buttonPressed = UIUtil.getSubmitButton(request, "submit_next");

        // Firstly, check for a click of the cancel button.
        if (buttonPressed.equals("submit_cancel"))
        {
            doCancellation(request,
                response,
                subInfo,
                EDIT_METADATA_2,
                EDIT_METADATA_2);
            return;
        }

        Item item = subInfo.submission.getItem();

        // Update object from form values
        readText(request, item, "subject", null, true, "en");
        readText(request, item, "description", "abstract", false, "en");
        readText(request, item, "description", "sponsorship", false, "en");
        readText(request, item, "description", null, false, "en");

        // Proceed according to button pressed
        int nextStep = -1;

        if (buttonPressed.equals("submit_subject_more"))
        {
            // "Add more" clicked for subject keywords
            subInfo.moreBoxesFor = "subject";
            subInfo.jumpToField = "subject";
            nextStep = EDIT_METADATA_2;
        }
        else if (buttonPressed.equals("submit_prev"))
        {
            nextStep = EDIT_METADATA_1;
        }
        else if (buttonPressed.equals("submit_next"))
        {
            userHasReached(subInfo, UPLOAD_FILES);
            nextStep = UPLOAD_FILES;
        }
        else if (buttonPressed.indexOf("remove") > -1)
        {
            // Remove button pressed - stay with same form
            nextStep = EDIT_METADATA_2;
        }

        // Write changes to database
        subInfo.submission.update();

        if (nextStep != -1)
        {
            doStep(context, request, response, subInfo, nextStep);
        }
        else
        {
            doStepJump(context, request, response, subInfo);
        }

        context.complete();
    }


    /**
     * Process the input from the choose file page
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     */
    private void processChooseFile(Context context,
        HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        // Wrap multipart request to get the submission info
        File temp = null;
        FileUploadRequest wrapper = new FileUploadRequest(request);
        SubmissionInfo subInfo = getSubmissionInfo(context, wrapper);
        String buttonPressed = UIUtil.getSubmitButton(wrapper, "submit_next");

        if (subInfo == null)
        {
            log.warn(LogManager.getHeader(context,
                "integrity_error",
                UIUtil.getRequestLogInfo(request)));
            JSPManager.showIntegrityError(request, response);
            return;
        }

        Item item = subInfo.submission.getItem();

        if (buttonPressed.equals("submit_cancel"))
        {
            doCancellation(request,
                response,
                subInfo,
                SubmitServlet.CHOOSE_FILE,
                SubmitServlet.UPLOAD_FILES);
        }
        else if (buttonPressed.equals("submit_prev"))
        {
            // Slightly tricky; if the user clicks on "previous,"
            // and they haven't uploaded any files yet, the previous
            // screen is the edit metadata 2 page.  If there are
            // upload files, we go back to the file list page,
            // without uploading the file they've specified.
            if (item.getBundles().length > 0)
            {
                // Have files, go to list
                showUploadFileList(request,
                    response,
                    subInfo,
                    false,
                    false);
            }
            else
            {
                // No files, go back to edit metadata 2
                doStep(context, request, response, subInfo, EDIT_METADATA_2);
            }
        }
        else if (buttonPressed.equals("submit_next"))
        {
            boolean ok = false;
            Bitstream b = null;
            BitstreamFormat bf = null;

            try
            {
                temp = wrapper.getFile("file");

                if (temp == null)
                {
                    // No file specified
                    doStep(context, request, response, subInfo, UPLOAD_FILES);
                    return;
                }

                // Read the temp file into a bitstream
                InputStream is = new BufferedInputStream(new FileInputStream(
                    temp));
                b = item.createSingleBitstream(is);

                // Strip all but the last filename.  It would be nice
                // to know which OS the file came from.
                String noPath = wrapper.getFilesystemName("file");
                while (noPath.indexOf('/') > -1)
                {
                    noPath = noPath.substring(
                                noPath.indexOf('/') + 1);
                }
                while (noPath.indexOf('\\') > -1)
                {
                    noPath = noPath.substring(
                                noPath.indexOf('\\') + 1);
                }

                b.setName(noPath);
                b.setSource(wrapper.getFilesystemName("file"));
                b.setDescription(wrapper.getParameter("description"));

                // Identify the format
                bf = FormatIdentifier.guessFormat(context, b);
                b.setFormat(bf);

                // Update to DB
                b.update();
                item.update();

				// Remove temporary file
				temp.delete();

                ok = true;
            }
            catch (IOException ie)
            {
                // Error storing bitstream - log it, leave ok=false
                log.warn(LogManager.getHeader(context,
                    "upload_error",
                    ""),
                    ie);
            }

            if (ok)
            {
                // Uploaded etc. OK
                if (bf != null)
                {
                    // Format was identified
                    showUploadFileList(request, response, subInfo, true, false);
                }
                else
                {
                    // Format couldn't be identified
                    showGetFileFormat(context, request, response, subInfo, b);
                }
                context.complete();
            }
            else
            {
                request.setAttribute("submission.info", subInfo);
                JSPManager.showJSP(request,
                    response,
                    "/submit/upload_error.jsp");
            }
        }
        else
        {
            doStepJump(context, request, response, subInfo);
        }

		// Remove temp file if it's still around
		if (temp != null)
		{
			temp.delete();
		}
    }


    /**
     * Process input from get file type page
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processGetFileFormat(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        String buttonPressed = UIUtil.getSubmitButton(request, "submit");
<commit_msg>Improved file upload error handling - an interrupted upload is no longer treated as an internal server error.<commit_after>/*
 * SubmitServlet.java
 *
 * Version: $Revision$
 *
 * Date: $Date$
 *
 * Copyright (c) 2001, Hewlett-Packard Company and Massachusetts
 * Institute of Technology.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the Hewlett-Packard Company nor the name of the
 * Massachusetts Institute of Technology nor the names of their
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
package org.dspace.app.webui.servlet;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import org.dspace.app.webui.util.Authenticate;
import org.dspace.app.webui.util.FileUploadRequest;
import org.dspace.app.webui.util.JSPManager;
import org.dspace.app.webui.util.SubmissionInfo;
import org.dspace.app.webui.util.UIUtil;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DCDate;
import org.dspace.content.DCPersonName;
import org.dspace.content.DCSeriesNumber;
import org.dspace.content.DCValue;
import org.dspace.content.FormatIdentifier;
import org.dspace.content.InProgressSubmission;
import org.dspace.content.Item;
import org.dspace.content.WorkspaceItem;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.eperson.EPerson;
import org.dspace.workflow.WorkflowItem;
import org.dspace.workflow.WorkflowManager;


/**
 * Submission servlet for DSpace.  Handles the initial submission of items, as
 * well as the editing of items further down the line.
 * <P>
 * Whenever the submit servlet receives a GET request, this is taken to
 * indicate the start of a fresh new submission, where no collection has been
 * selected, and the submission process is started from scratch.
 * <P>
 * All other interactions happen via POSTs.  Part of the post will normally be
 * a (hidden) "step" parameter, which will correspond to the form that the
 * user has just filled out.  If this is absent, step 0 (select collection)
 * is assumed, meaning that it's simple to place "Submit to this collection"
 * buttons on collection home pages.
 * <P>
 * According to the step number of the incoming form, the values posted from
 * the form are processed (using the process* methods), and the item updated
 * as appropriate.  The servlet then forwards control of the request to the
 * appropriate JSP (from jsp/submit) to render the next stage
 * of the process or an error if appropriate.  Each of these JSPs may
 * require that attributes be passed in.  Check the comments at the top of a
 * JSP to see which attributes are needed.  All submit-related forms require a
 * properly initialised SubmissionInfo object to be present in the the
 * "submission.info" attribute.  This holds the core information relevant to
 * the submission, e.g. the item, personal workspace or workflow item, the
 * submitting "e-person", and the target collection.
 * <P>
 * When control of the request reaches a JSP, it is assumed that all checks,
 * interactions with the database and so on have been performed and that all
 * necessary information to render the form is in memory.  e.g. The
 * SubmitFormInfo object passed in must be correctly filled out.  Thus the
 * JSPs do no error or integrity checking; it is the servlet's responsibility
 * to ensure that everything is prepared.  The servlet is fairly diligent
 * about ensuring integrity at each step.
 * <P>
 * Each step has an integer constant defined below.  The main sequence of the
 * submission procedure always runs from 0 upwards, until SUBMISSION_COMPLETE.
 * Other, not-in-sequence steps (such as the cancellation screen and the
 * "previous version ID verification" screen) have numbers much higher than
 * SUBMISSION_COMPLETE.  These conventions allow the progress bar component
 * of the submission forms to render the user's progress through the process.
 *
 * @author  <NAME>
 * @version $Revision$
 */
public class SubmitServlet extends DSpaceServlet
{
    // Steps in the submission process

    /** Selection collection step */
    public static final int SELECT_COLLECTION = 0;

    /** Ask initial questions about the submission step */
    public static final int INITIAL_QUESTIONS = 1;

    /** Edit DC metadata page 1 step */
    public static final int EDIT_METADATA_1 = 2;

    /** Edit DC metadata page 2 step */
    public static final int EDIT_METADATA_2 = 3;

    /**
     * Upload files step.  Note this doesn't correspond to an actual
     * page, since the upload file step consists of a number of
     * pages with no definite order.  This is just used for the progress
     * bar.
     */
    public static final int UPLOAD_FILES = 4;

    /** Review submission step */
    public static final int REVIEW_SUBMISSION = 5;

    /** Grant license step */
    public static final int GRANT_LICENSE = 6;

    /** Submission completed step */
    public static final int SUBMISSION_COMPLETE = 7;

    // Steps which aren't part of the main sequence, but rather
    // short "diversions" are given high step numbers.  The main sequence
    // is defined as being steps 0 to SUBMISSION_COMPLETE.

    /** Cancellation of a submission */
    public static final int SUBMISSION_CANCELLED = 101;

    /** List of uploaded files */
    public static final int FILE_LIST = 102;

    /** Choose file page */
    public static final int CHOOSE_FILE = 103;

    /** File format page */
    public static final int GET_FILE_FORMAT = 104;

    /** Error uploading file */
    public static final int UPLOAD_ERROR = 105;

    /** Change file description page */
    public static final int CHANGE_FILE_DESCRIPTION = 106;

    /**
     * Verify pruning of extra files, titles, dates as a result of
     * changing an answer to one of the initial questions
     */
    public static final int VERIFY_PRUNE = 107;

    /** log4j logger */
    private static Logger log = Logger.getLogger(SubmitServlet.class);


    protected void doDSGet(Context context,
        HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        /*
         * Possible GET parameters:
         *
         *   resume=<workspace_item_id>
         *         - Resumes submitting the given workspace item
         *
         *   workflow=<workflow_id>
         *         - Starts editing the given workflow item in workflow mode
         *
         * With no parameters, A GET starts a new submission.  What happens
         * depends on the context of the user (where they are.)  If they're not
         * in a community or collection, they can choose any collection
         * from the list of all collections.  If they're in a community,
         * the list of collections they can choose from will be limited to
         * those within the current community.  If the user has selected
         * a collection, a new submission will be started in that collection.
         */

        String workspaceID = request.getParameter("resume");
        if (workspaceID != null)
        {
            try
            {
                WorkspaceItem wi = WorkspaceItem.find(context,
                    Integer.parseInt(workspaceID));

                SubmissionInfo si = new SubmissionInfo();
                si.submission = wi;
                doStep(context, request, response, si, INITIAL_QUESTIONS);
            }
            catch (NumberFormatException nfe)
            {
                log.warn(LogManager.getHeader(context,
                    "bad_workspace_id",
                    "bad_id=" + workspaceID));
                JSPManager.showInvalidIDError(request,
                    response,
                    workspaceID,
                    -1);
            }
            return;
        }

        String workflowID = request.getParameter("workflow");
        if (workflowID != null)
        {
            try
            {
                WorkflowItem wi = WorkflowItem.find(context,
                    Integer.parseInt(workflowID));

                SubmissionInfo si = new SubmissionInfo();
                si.submission = wi;
                doStep(context, request, response, si, INITIAL_QUESTIONS);
            }
            catch (NumberFormatException nfe)
            {
                log.warn(LogManager.getHeader(context,
                    "bad_workflow_id",
                    "bad_id=" + workflowID));
                JSPManager.showInvalidIDError(request,
                    response,
                    workflowID,
                    -1);
            }
            return;
        }



        Community  com = UIUtil.getCommunityLocation (request);
        Collection col = UIUtil.getCollectionLocation(request);

        if (col != null)
        {
            // In a collection, skip the "choose selection" stage
            // Create a workspace item
            WorkspaceItem wi = WorkspaceItem.create(context, col, true);

            // Proceed to first step
            SubmissionInfo si = new SubmissionInfo();
            si.submission = wi;
            doStep(context, request, response, si, INITIAL_QUESTIONS);
            context.complete();
        }
        else
        {
            Collection[] collections;

            if (com != null)
            {
                // In a community.  Show collections in that community only.
                collections = com.getCollections();
            }
            else
            {
                // Show all collections
                collections = Collection.findAll(context);
            }

            log.info(LogManager.getHeader(context,
                "select_collection",
                ""));

            request.setAttribute("collections", collections);
            JSPManager.showJSP(request, response,
                "/submit/select-collection.jsp");
        }
    }


    protected void doDSPost(Context context,
        HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        // First of all, we need to work out if this is a multipart request
        // The file upload page uses those
        String contentType = request.getContentType();

        if (contentType != null &&
            contentType.indexOf("multipart/form-data") != -1)
        {
            // This is a multipart request, so it's a file upload
            processChooseFile(context, request, response);
            return;
        }

        // First get the step
        int step = UIUtil.getIntParameter(request, "step");

        // select collection is a special case - no submissioninfo object
        // If no step was given, we also assume "select collection"
        // (Submit button on collection home page or elsewhere)
        if (step == SELECT_COLLECTION || step == -1)
        {
            processSelectCollection(context, request, response);
            return;
        }

        // Get submission info
        SubmissionInfo subInfo = getSubmissionInfo(context, request);

        if (subInfo == null)
        {
            /*
             * Work around for problem where people select "is a thesis",
             * see the error page, and then use their "back" button thinking
             * they can start another submission - it's been removed so the ID
             * in the form is invalid.  If we detect the "removed_thesis"
             * attribute we display a friendly message instead of an integrity
             * error.
             */
            if (request.getSession().getAttribute("removed_thesis") != null)
            {
                request.getSession().removeAttribute("removed_thesis");
                JSPManager.showJSP(request,
                    response,
                    "/submit/thesis-removed-workaround.jsp");
                return;
            }
            else
            {
                /*
                 * If the submission info was invalid, set the step to an
                 * invalid number so an integrity error will be shown
                 */
                step = -1;
            }
        }


        switch (step)
        {
        case INITIAL_QUESTIONS:
            processInitialQuestions(context, request, response, subInfo);
            break;

        case EDIT_METADATA_1:
            processEditMetadataOne(context, request, response, subInfo);
            break;

        case EDIT_METADATA_2:
            processEditMetadataTwo(context, request, response, subInfo);
            break;

        case GET_FILE_FORMAT:
            processGetFileFormat(context, request, response, subInfo);
            break;

        case FILE_LIST:
            processFileList(context, request, response, subInfo);
            break;

        case UPLOAD_ERROR:
            processUploadError(context, request, response, subInfo);
            break;

        case CHANGE_FILE_DESCRIPTION:
            processChangeFileDescription(context, request, response, subInfo);
            break;

        case REVIEW_SUBMISSION:
            processReview(context, request, response, subInfo);
            break;

        case GRANT_LICENSE:
            processLicense(context, request, response, subInfo);
            break;

        case SUBMISSION_CANCELLED:
            processCancellation(context, request, response, subInfo);
            break;

        case VERIFY_PRUNE:
            processVerifyPrune(context, request, response, subInfo);
            break;

        default:
            log.warn(LogManager.getHeader(context,
                "integrity_error",
                UIUtil.getRequestLogInfo(request)));
            JSPManager.showIntegrityError(request, response);
        }
    }


    //****************************************************************
    //****************************************************************
    //             METHODS FOR PROCESSING POSTED FORMS
    //****************************************************************
    //****************************************************************

    /**
     * Process the selection collection stage, or the clicking of a
     * "submit to this collection" button on a collection home page.
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     */
    private void processSelectCollection(Context context,
        HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        // The user might have clicked cancel.  We don't do a
        // standard cancellation at this stage, since we don't
        // actually have an item to keep or remove yet.
        if (request.getParameter("submit_cancel") != null)
        {
            // Just send them to their "My DSpace" for now.
            response.sendRedirect(response.encodeRedirectURL(
                    request.getContextPath() + "/mydspace"));
            return;
        }

        // First we find the collection
        int id = UIUtil.getIntParameter(request, "collection");
        Collection col = Collection.find(context, id);

        // Show an error if we don't have a collection
        if (col == null)
        {
            JSPManager.showInvalidIDError(request,
                response,
                request.getParameter("collection"),
                Constants.COLLECTION);
        }
        else
        {
            WorkspaceItem wi = WorkspaceItem.create(context, col, true);

            // Proceed to first step
            SubmissionInfo si = new SubmissionInfo();
            si.submission = wi;
            doStep(context, request, response, si, INITIAL_QUESTIONS);

            context.complete();
        }
    }


    /**
     * process input from initial-questions.jsp
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processInitialQuestions(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        String buttonPressed = UIUtil.getSubmitButton(request, "submit_next");

        // Firstly, check for a click of the cancel button.
        if (buttonPressed.equals("submit_cancel"))
        {
            doCancellation(request,
                response,
                subInfo,
                INITIAL_QUESTIONS,
                INITIAL_QUESTIONS);
            return;
        }

        // Get the values from the form
        boolean multipleTitles =
            UIUtil.getBoolParameter(request, "multiple_titles");
        boolean publishedBefore =
            UIUtil.getBoolParameter(request, "published_before");
        boolean multipleFiles =
            UIUtil.getBoolParameter(request, "multiple_files");
        boolean isThesis =
            UIUtil.getBoolParameter(request, "is_thesis");

        if (isWorkflow(subInfo))
        {
            // Thesis question does not appear in workflow mode..
            isThesis = false;
            // Pretend "multiple files" is true in workflow mode
            // (There will always be the license file)
            multipleFiles = true;
        }

        // First and foremost - if it's a thesis, reject the submission
        // FIXME: MIT-SPECIFIC
        if (isThesis)
        {
            WorkspaceItem wi = (WorkspaceItem) subInfo.submission;
            wi.deleteAll();

            // Remember that we've removed a thesis in the session
            request.getSession().setAttribute(
                "removed_thesis",
                new Boolean(true));

            // Display appropriate message
            JSPManager.showJSP(request, response, "/submit/no-theses.jsp");

            context.complete();
            return;
        }

        // Now check to see if the changes will remove any values
        // (i.e. multiple files, titles or an issue date.)
        boolean willRemoveTitles = false;
        boolean willRemoveDate = false;
        boolean willRemoveFiles = false;

        if (multipleTitles == false)
        {
            DCValue[] altTitles = subInfo.submission.getItem().getDC(
                "title", "alternative", Item.ANY);

            willRemoveTitles = altTitles.length > 0;
        }

        if (publishedBefore == false)
        {
            DCValue[] dateIssued = subInfo.submission.getItem().getDC(
                "date", "issued", Item.ANY);

            willRemoveDate = dateIssued.length > 0;
        }

        if (multipleFiles == false)
        {
            // FIXME: Assuming each bundle has but one bitstream in it
            Bundle[] bundles = subInfo.submission.getItem().getBundles();

            willRemoveFiles = bundles.length > 1;
        }

        // If anything is going to be removed from the item as a result
        // of changing the answer to one of the questions, we need
        // to inform the user and make sure that's OK
        if (willRemoveTitles || willRemoveDate || willRemoveFiles)
        {
            // Verify pruning of extra bits
            request.setAttribute("submission.info", subInfo);
            request.setAttribute(
                "multiple.titles", new Boolean(multipleTitles));
            request.setAttribute(
                "published.before", new Boolean(publishedBefore));
            request.setAttribute(
                "multiple.files", new Boolean(multipleFiles));
            request.setAttribute(
                "will.remove.titles", new Boolean(willRemoveTitles));
            request.setAttribute(
                "will.remove.date", new Boolean(willRemoveDate));
            request.setAttribute(
                "will.remove.files", new Boolean(willRemoveFiles));
            request.setAttribute("button.pressed",
                UIUtil.getSubmitButton(request, "submit_next"));

            JSPManager.showJSP(request, response, "/submit/verify-prune.jsp");
        }
        else
        {
            // Nothing needs removing, so just make the changes
            subInfo.submission.setMultipleTitles(multipleTitles);
            subInfo.submission.setPublishedBefore(publishedBefore);

            // "Multiple files" irrelevant in workflow mode
            if (!isWorkflow(subInfo))
            {
                subInfo.submission.setMultipleFiles(multipleFiles);
            }

            subInfo.submission.update();

            // On to the next stage
            if (buttonPressed.equals("submit_next"))
            {
                // Update user's progress
                userHasReached(subInfo, EDIT_METADATA_1);

                // User has clicked "Next"
                doStep(context, request, response, subInfo, EDIT_METADATA_1);
            }
            else
            {
                // Progress bar button clicked
                doStepJump(context, request, response, subInfo);
            }

            context.complete();
        }
    }


    /**
     * Process input from "verify prune" step
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processVerifyPrune(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        if (request.getParameter("do_not_proceed") != null)
        {
            // User cancelled
            doStep(context, request, response, subInfo, INITIAL_QUESTIONS);
            return;
        }

        // User elected to proceed - do the pruning
        // Get the values from the form
        boolean multipleTitles =
            UIUtil.getBoolParameter(request, "multiple_titles");
        boolean publishedBefore =
            UIUtil.getBoolParameter(request, "published_before");

        // Multiple files question does not appear in workflow mode.
        // Since the submission will have a license, the answer to
        // this question will always be "yes"
        boolean multipleFiles = (isWorkflow(subInfo) ||
            UIUtil.getBoolParameter(request, "multiple_files"));

        if (!multipleTitles)
        {
            subInfo.submission.getItem().clearDC("title",
                "alternative", Item.ANY);
        }

        if (publishedBefore == false)
        {
            subInfo.submission.getItem().clearDC("date", "issued", Item.ANY);
        }

        if (multipleFiles == false)
        {
            // FIXME: Assuming each bundle has but one bitstream in it
            Item item = subInfo.submission.getItem();
            Bundle[] bundles = item.getBundles();

            // Remove all but the first bundle
            for (int i = 1; i < bundles.length; i++)
            {
                item.removeBundle(bundles[i]);
            }
        }

        // Nothing needs removing, so just make the changes
        subInfo.submission.setMultipleTitles(multipleTitles);
        subInfo.submission.setPublishedBefore(publishedBefore);

        // "Multiple files" irrelevant in workflow mode
        if (!isWorkflow(subInfo))
        {
            subInfo.submission.setMultipleFiles(multipleFiles);
        }

        subInfo.submission.update();

        // Everything went OK if we get to here, so now response
        // to the original button press
        if (request.getParameter("submit_next") != null)
        {
            // Update user's progress
            userHasReached(subInfo, EDIT_METADATA_1);

            // User has clicked "Next"
            doStep(context, request, response, subInfo, EDIT_METADATA_1);

            context.complete();
        }
        else
        {
            // Progress bar button clicked
            doStepJump(context, request, response, subInfo);

            context.complete();
        }
    }


    /**
     * process input from edit-metadata-1.jsp
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processEditMetadataOne(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        String buttonPressed = UIUtil.getSubmitButton(request, "submit_next");

        // Firstly, check for a click of the cancel button.
        if (buttonPressed.equals("submit_cancel"))
        {
            doCancellation(request,
                response,
                subInfo,
                EDIT_METADATA_1,
                EDIT_METADATA_1);
            return;
        }

        Item item = subInfo.submission.getItem();

        // Update the item metadata.
        readNames(request, item, "contributor", "author", true);
        readText(request, item, "title", null, false, "en");

        if (subInfo.submission.hasMultipleTitles())
        {
            readText(request, item, "title", "alternative", true, "en");
        }

        if (subInfo.submission.isPublishedBefore())
        {
            readDate(request, item, "date", "issued");
        }

        readSeriesNumbers(request, item, "relation", "ispartofseries", true);

        // FIXME: Maybe should do integrity check using language object
        readText(request, item, "language", "iso", false, null);

        // Identifiers - A special case so do this here
        // First, remove existing identifiers.
        subInfo.submission.getItem().clearDC("identifier", Item.ANY, Item.ANY);

        // Get qualifiers and values
        List quals = getRepeatedParameter(request, "identifier_qualifier");
        List vals = getRepeatedParameter(request, "identifier_value");

        // Add to item DC
        for (int i = 0; i<vals.size(); i++)
        {
            String thisQual = (String) quals.get(i);
            String thisVal = (String) vals.get(i);

            if (!buttonPressed.equals("submit_identifier_remove_" + i) &&
                !thisVal.equals(""))
            {
                item.addDC("identifier", thisQual, null, thisVal);
            }
        }

        int nextStep = -1;

        // Proceed according to button pressed
        if (buttonPressed.equals("submit_contributor_author_more"))
        {
            // "Add more" clicked for authors
            subInfo.moreBoxesFor = "contributor_author";
            subInfo.jumpToField = "contributor_author";
            nextStep = EDIT_METADATA_1;
        }
        else if (subInfo.submission.hasMultipleTitles() &&
            buttonPressed.equals("submit_title_alternative_more"))
        {
            // "Add more" clicked for alternative titles
            subInfo.moreBoxesFor = "title_alternative";
            subInfo.jumpToField = "title_alternative";
            nextStep = EDIT_METADATA_1;
        }
        else if (buttonPressed.equals("submit_relation_ispartofseries_more"))
        {
            // "Add more" clicked for series/number
            subInfo.moreBoxesFor = "relation_ispartofseries";
            subInfo.jumpToField = "relation_ispartofseries";
            nextStep = EDIT_METADATA_1;
        }
        else if (buttonPressed.equals("submit_identifier_more"))
        {
            // "Add more" clicked for identifier
            subInfo.moreBoxesFor = "identifier";
            subInfo.jumpToField = "identifier";
            nextStep = EDIT_METADATA_1;
        }
        else if (buttonPressed.equals("submit_prev"))
        {
            nextStep = INITIAL_QUESTIONS;
        }
        else if (buttonPressed.equals("submit_next"))
        {
            userHasReached(subInfo, EDIT_METADATA_2);
            nextStep = EDIT_METADATA_2;
        }
        else if (buttonPressed.indexOf("remove") > -1)
        {
            // Remove button pressed - stay with same form
            nextStep = EDIT_METADATA_1;
        }

        // Write changes to database
        subInfo.submission.update();

        if (nextStep != -1)
        {
            doStep(context, request, response, subInfo, nextStep);
        }
        else
        {
            doStepJump(context, request, response, subInfo);
        }

        context.complete();
    }


    /**
     * process input from edit-metadata-2.jsp
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processEditMetadataTwo(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        String buttonPressed = UIUtil.getSubmitButton(request, "submit_next");

        // Firstly, check for a click of the cancel button.
        if (buttonPressed.equals("submit_cancel"))
        {
            doCancellation(request,
                response,
                subInfo,
                EDIT_METADATA_2,
                EDIT_METADATA_2);
            return;
        }

        Item item = subInfo.submission.getItem();

        // Update object from form values
        readText(request, item, "subject", null, true, "en");
        readText(request, item, "description", "abstract", false, "en");
        readText(request, item, "description", "sponsorship", false, "en");
        readText(request, item, "description", null, false, "en");

        // Proceed according to button pressed
        int nextStep = -1;

        if (buttonPressed.equals("submit_subject_more"))
        {
            // "Add more" clicked for subject keywords
            subInfo.moreBoxesFor = "subject";
            subInfo.jumpToField = "subject";
            nextStep = EDIT_METADATA_2;
        }
        else if (buttonPressed.equals("submit_prev"))
        {
            nextStep = EDIT_METADATA_1;
        }
        else if (buttonPressed.equals("submit_next"))
        {
            userHasReached(subInfo, UPLOAD_FILES);
            nextStep = UPLOAD_FILES;
        }
        else if (buttonPressed.indexOf("remove") > -1)
        {
            // Remove button pressed - stay with same form
            nextStep = EDIT_METADATA_2;
        }

        // Write changes to database
        subInfo.submission.update();

        if (nextStep != -1)
        {
            doStep(context, request, response, subInfo, nextStep);
        }
        else
        {
            doStepJump(context, request, response, subInfo);
        }

        context.complete();
    }


    /**
     * Process the input from the choose file page
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     */
    private void processChooseFile(Context context,
        HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        File temp = null;
        FileUploadRequest wrapper = null;
        SubmissionInfo subInfo = null;
        boolean ok = false;
        String buttonPressed = null;
        BitstreamFormat bf = null;
        Bitstream b = null;

        /*
         * To distinguish between an IOException caused by a problem with the
         * file upload and one caused by displaying a JSP, we do all the upload
         * handling in a try/catch block
         */
        
        try
        {
            // Wrap multipart request to get the submission info
            wrapper = new FileUploadRequest(request);

            subInfo = getSubmissionInfo(context, wrapper);
            buttonPressed = UIUtil.getSubmitButton(wrapper, "submit_next");

            if (subInfo != null && buttonPressed.equals("submit_next"))
            {
                // Create the bitstream
                Item item = subInfo.submission.getItem();

                temp = wrapper.getFile("file");

                if (temp != null)
                {
                    // Read the temp file into a bitstream
                    InputStream is = new BufferedInputStream(
                        new FileInputStream(temp));
                    b = item.createSingleBitstream(is);

                    // Strip all but the last filename.  It would be nice
                    // to know which OS the file came from.
                    String noPath = wrapper.getFilesystemName("file");
                    while (noPath.indexOf('/') > -1)
                    {
                        noPath = noPath.substring(
                                    noPath.indexOf('/') + 1);
                    }
                    while (noPath.indexOf('\\') > -1)
                    {
                        noPath = noPath.substring(
                                    noPath.indexOf('\\') + 1);
                    }

                    b.setName(noPath);
                    b.setSource(wrapper.getFilesystemName("file"));
                    b.setDescription(wrapper.getParameter("description"));

                    // Identify the format
                    bf = FormatIdentifier.guessFormat(context, b);
                    b.setFormat(bf);

                    // Update to DB
                    b.update();
                    item.update();
                    
                    ok = true;
                }
            }
        }
        catch (IOException ie)
        {
            // Problem with uploading
            log.warn(LogManager.getHeader(context,
                "upload_error",
                ""),
                ie);
        }
        
        if (subInfo == null)
        {
            // In any event, if we don't have the submission info, the request
            // was malformed
            log.warn(LogManager.getHeader(context,
                "integrity_error",
                UIUtil.getRequestLogInfo(request)));
        }
        else if (buttonPressed.equals("submit_cancel"))
        {
            doCancellation(request,
                response,
                subInfo,
                SubmitServlet.CHOOSE_FILE,
                SubmitServlet.UPLOAD_FILES);
        }
        else if (buttonPressed.equals("submit_prev"))
        {
            // Slightly tricky; if the user clicks on "previous,"
            // and they haven't uploaded any files yet, the previous
            // screen is the edit metadata 2 page.  If there are
            // upload files, we go back to the file list page,
            // without uploading the file they've specified.
            if (subInfo.submission.getItem().getBundles().length > 0)
            {
                // Have files, go to list
                showUploadFileList(request,
                    response,
                    subInfo,
                    false,
                    false);
            }
            else
            {
                // No files, go back to edit metadata 2
                doStep(context, request, response, subInfo, EDIT_METADATA_2);
            }
        }
        else if (buttonPressed.equals("submit_next"))
        {
            // "Next" pressed - the actual upload was handled above.
            if (ok)
            {
                // Uploaded etc. OK
                if (bf != null)
                {
                    // Format was identified
                    showUploadFileList(request, response, subInfo, true, false);
                }
                else
                {
                    // Format couldn't be identified
                    showGetFileFormat(context, request, response, subInfo, b);
                }
                context.complete();
            }
            else
            {
                // If we get here, there was a problem uploading, but we
                // still know which submission we're dealing with
                request.setAttribute("submission.info", subInfo);
                JSPManager.showJSP(request,
                    response,
                    "/submit/upload-error.jsp");
            }
        }
        else
        {
            doStepJump(context, request, response, subInfo);
        }

	// Remove temp file if it's still around
	if (temp != null)
	{
		temp.delete();
	}
    }


    /**
     * Process input from get file type page
     *
     * @param context   current DSpace context
     * @param request   current servlet request object
     * @param response  current servlet response object
     * @param subInfo   submission info object
     */
    private void processGetFileFormat(Context context,
        HttpServletRequest request,
        HttpServletResponse response,
        SubmissionInfo subInfo)
        throws ServletException, IOException, SQLException, AuthorizeException
    {
        String buttonPressed = UIUtil.getSubmitButton(request, "submit");

{'fraction_non_alphanumeric': 0.08607704995588743, 'fraction_numerical': 0.002250380391003593, 'mean_word_length': 1.916977472773385, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3106186', 'n_tokens_mistral': 19962, 'n_tokens_neox': 18961, 'n_words': 6042}