001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ade.upload;
029
030import org.opencms.db.CmsDbSqlException;
031import org.opencms.db.CmsImportFolder;
032import org.opencms.file.CmsFile;
033import org.opencms.file.CmsObject;
034import org.opencms.file.CmsProperty;
035import org.opencms.file.CmsPropertyDefinition;
036import org.opencms.file.CmsResource;
037import org.opencms.file.CmsResourceFilter;
038import org.opencms.file.collectors.A_CmsResourceCollector;
039import org.opencms.file.collectors.I_CmsCollectorPostCreateHandler;
040import org.opencms.file.types.CmsResourceTypeFolder;
041import org.opencms.file.types.CmsResourceTypePlain;
042import org.opencms.gwt.shared.CmsUploadRestrictionInfo;
043import org.opencms.gwt.shared.I_CmsUploadConstants;
044import org.opencms.i18n.CmsMessages;
045import org.opencms.json.JSONArray;
046import org.opencms.json.JSONException;
047import org.opencms.json.JSONObject;
048import org.opencms.jsp.CmsJspBean;
049import org.opencms.loader.CmsLoaderException;
050import org.opencms.lock.CmsLockException;
051import org.opencms.main.CmsException;
052import org.opencms.main.CmsLog;
053import org.opencms.main.OpenCms;
054import org.opencms.security.CmsSecurityException;
055import org.opencms.util.CmsCollectionsGenericWrapper;
056import org.opencms.util.CmsPair;
057import org.opencms.util.CmsRequestUtil;
058import org.opencms.util.CmsStringUtil;
059import org.opencms.util.CmsUUID;
060
061import java.io.File;
062import java.io.UnsupportedEncodingException;
063import java.net.URLDecoder;
064import java.util.ArrayList;
065import java.util.Collections;
066import java.util.HashMap;
067import java.util.List;
068import java.util.Map;
069
070import javax.servlet.http.HttpServletRequest;
071import javax.servlet.http.HttpServletResponse;
072import javax.servlet.jsp.PageContext;
073
074import org.apache.commons.fileupload.FileItem;
075import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
076import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
077import org.apache.commons.fileupload.disk.DiskFileItemFactory;
078import org.apache.commons.fileupload.servlet.ServletFileUpload;
079import org.apache.commons.lang3.StringUtils;
080import org.apache.commons.logging.Log;
081
082/**
083 * Bean to be used in JSP scriptlet code that provides
084 * access to the upload functionality.<p>
085 *
086 * @since 8.0.0
087 */
088public class CmsUploadBean extends CmsJspBean {
089
090    /** The default upload timeout. */
091    public static final int DEFAULT_UPLOAD_TIMEOUT = 20000;
092
093    /** Key name for the session attribute that stores the id of the current listener. */
094    public static final String SESSION_ATTRIBUTE_LISTENER_ID = "__CmsUploadBean.LISTENER";
095
096    /** The log object for this class. */
097    private static final Log LOG = CmsLog.getLog(CmsUploadBean.class);
098
099    /** A static map of all listeners. */
100    private static Map<CmsUUID, CmsUploadListener> m_listeners = new HashMap<CmsUUID, CmsUploadListener>();
101
102    /** The gwt message bundle. */
103    private CmsMessages m_bundle = org.opencms.ade.upload.Messages.get().getBundle();
104
105    /** Signals that the start method is called. */
106    private boolean m_called;
107
108    /** A list of the file items to upload. */
109    private List<FileItem> m_multiPartFileItems;
110
111    /** The map of parameters read from the current request. */
112    private Map<String, String[]> m_parameterMap;
113
114    /** The names by id of the resources that have been created successfully. */
115    private HashMap<CmsUUID, String> m_resourcesCreated = new HashMap<CmsUUID, String>();
116
117    /** A CMS context for the root site. */
118    private CmsObject m_rootCms;
119
120    /** The server side upload delay. */
121    private int m_uploadDelay;
122
123    /** The upload hook URI. */
124    private String m_uploadHook;
125
126    private CmsUploadRestrictionInfo m_uploadRestrictionInfo;
127
128    /**
129     * Constructor, with parameters.<p>
130     *
131     * @param context the JSP page context object
132     * @param req the JSP request
133     * @param res the JSP response
134     *
135     * @throws CmsException if something goes wrong
136     */
137    public CmsUploadBean(PageContext context, HttpServletRequest req, HttpServletResponse res)
138    throws CmsException {
139
140        super();
141        init(context, req, res);
142
143        m_rootCms = OpenCms.initCmsObject(getCmsObject());
144        m_rootCms.getRequestContext().setSiteRoot("");
145        m_uploadRestrictionInfo = OpenCms.getWorkplaceManager().getUploadRestriction().getUploadRestrictionInfo(
146            m_rootCms);
147    }
148
149    /**
150     * Returns the listener for given CmsUUID.<p>
151     *
152     * @param listenerId the uuid
153     *
154     * @return the according listener
155     */
156    public static CmsUploadListener getCurrentListener(CmsUUID listenerId) {
157
158        return m_listeners.get(listenerId);
159    }
160
161    /**
162     * Returns the VFS path for the given filename and folder.<p>
163     *
164     * @param cms the cms object
165     * @param fileName the filename to combine with the folder
166     * @param folder the folder to combine with the filename
167     * @param keepFileNames skip file name translation if true
168     *
169     * @return the VFS path for the given filename and folder
170     */
171    public static String getNewResourceName(CmsObject cms, String fileName, String folder, boolean keepFileNames) {
172
173        String newResname = CmsResource.getName(fileName.replace('\\', '/'));
174        if (!keepFileNames) {
175            newResname = cms.getRequestContext().getFileTranslator().translateResource(newResname);
176        }
177        newResname = folder + newResname;
178        return newResname;
179    }
180
181    /**
182     * Sets the uploadDelay.<p>
183     *
184     * @param uploadDelay the uploadDelay to set
185     */
186    public void setUploadDelay(int uploadDelay) {
187
188        m_uploadDelay = uploadDelay;
189    }
190
191    /**
192     * Starts the upload.<p>
193     *
194     * @return the response String (JSON)
195     */
196    public String start() {
197
198        // ensure that this method can only be called once
199        if (m_called) {
200            throw new UnsupportedOperationException();
201        }
202        m_called = true;
203
204        // create a upload listener
205        CmsUploadListener listener = createListener();
206        try {
207            // try to parse the request
208            parseRequest(listener);
209            // try to create the resources on the VFS
210            createResources(listener);
211            // trigger update offline indexes, important for gallery search
212            OpenCms.getSearchManager().updateOfflineIndexes();
213        } catch (CmsException e) {
214            // an error occurred while creating the resources on the VFS, create a special error message
215            LOG.error(e.getMessage(), e);
216            return generateResponse(Boolean.FALSE, getCreationErrorMessage(), formatStackTrace(e));
217        } catch (CmsUploadException e) {
218            // an expected error occurred while parsing the request, the error message is already set in the exception
219            LOG.debug(e.getMessage(), e);
220            return generateResponse(Boolean.FALSE, e.getMessage(), formatStackTrace(e));
221        } catch (Throwable e) {
222            // an unexpected error occurred while parsing the request, create a non-specific error message
223            LOG.error(e.getMessage(), e);
224            String message = m_bundle.key(org.opencms.ade.upload.Messages.ERR_UPLOAD_UNEXPECTED_0);
225            return generateResponse(Boolean.FALSE, message, formatStackTrace(e));
226        } finally {
227            removeListener(listener.getId());
228        }
229        // the upload was successful inform the user about success
230        return generateResponse(Boolean.TRUE, m_bundle.key(org.opencms.ade.upload.Messages.LOG_UPLOAD_SUCCESS_0), "");
231    }
232
233    /**
234     * Creates a upload listener and puts it into the static map.<p>
235     *
236     * @return the listener
237     */
238    private CmsUploadListener createListener() {
239
240        CmsUploadListener listener = new CmsUploadListener(getRequest().getContentLength());
241        listener.setDelay(m_uploadDelay);
242        m_listeners.put(listener.getId(), listener);
243        getRequest().getSession().setAttribute(SESSION_ATTRIBUTE_LISTENER_ID, listener.getId());
244        return listener;
245    }
246
247    /**
248     * Creates the resources.<p>
249     * @param listener the listener
250     *
251     * @throws CmsException if something goes wrong
252     * @throws UnsupportedEncodingException in case the encoding is not supported
253     */
254    private void createResources(CmsUploadListener listener) throws CmsException, UnsupportedEncodingException {
255
256        CmsObject cms = getCmsObject();
257        String[] isRootPathVals = m_parameterMap.get(I_CmsUploadConstants.UPLOAD_IS_ROOT_PATH_FIELD_NAME);
258        if ((isRootPathVals != null) && (isRootPathVals.length > 0) && Boolean.parseBoolean(isRootPathVals[0])) {
259            cms = m_rootCms;
260        }
261        // get the target folder
262        String targetFolder = getTargetFolder(cms);
263        m_uploadHook = OpenCms.getWorkplaceManager().getUploadHook(cms, targetFolder);
264
265        List<String> filesToUnzip = getFilesToUnzip();
266
267        // iterate over the list of files to upload and create each single resource
268        for (FileItem fileItem : m_multiPartFileItems) {
269            if ((fileItem != null) && (!fileItem.isFormField())) {
270                // read the content of the file
271                byte[] content = fileItem.get();
272                fileItem.delete();
273
274                // determine the new resource name
275                String fileName = m_parameterMap.get(
276                    fileItem.getFieldName() + I_CmsUploadConstants.UPLOAD_FILENAME_ENCODED_SUFFIX)[0];
277                fileName = URLDecoder.decode(fileName, "UTF-8");
278
279                if (filesToUnzip.contains(CmsResource.getName(fileName.replace('\\', '/')))) {
280                    // import the zip
281                    CmsImportFolder importZip = new CmsImportFolder();
282                    try {
283                        importZip.importZip(content, targetFolder, cms, false);
284                    } finally {
285                        // get the created resource names
286                        for (CmsResource importedResource : importZip.getImportedResources()) {
287                            m_resourcesCreated.put(importedResource.getStructureId(), importedResource.getName());
288                        }
289                    }
290                } else {
291                    // create the resource
292                    CmsResource importedResource = createSingleResource(cms, fileName, targetFolder, content);
293                    if (importedResource != null) {
294                        // add the name of the created resource to the list of successful created resources
295                        m_resourcesCreated.put(importedResource.getStructureId(), importedResource.getName());
296                    }
297                }
298
299                if (listener.isCanceled()) {
300                    throw listener.getException();
301                }
302            }
303        }
304
305        String postCreateHandlerStr = getPostCreateHandler();
306        if (postCreateHandlerStr != null) {
307            try {
308                CmsPair<String, String> classAndConfig = I_CmsCollectorPostCreateHandler.splitClassAndConfig(
309                    postCreateHandlerStr);
310                String className = classAndConfig.getFirst();
311                String config = classAndConfig.getSecond();
312                I_CmsCollectorPostCreateHandler handler = A_CmsResourceCollector.getPostCreateHandler(className);
313                for (Map.Entry<CmsUUID, String> resourceEntry : m_resourcesCreated.entrySet()) {
314                    try {
315                        CmsUUID structureId = resourceEntry.getKey();
316                        CmsResource resource = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
317                        if (!resource.isFolder()) {
318                            handler.onCreate(cms, resource, false, config);
319                        }
320                    } catch (Exception e) {
321                        LOG.error(e.getLocalizedMessage(), e);
322                    }
323                }
324            } catch (Exception e) {
325                LOG.error(e.getLocalizedMessage(), e);
326            }
327
328        }
329    }
330
331    /**
332     * Creates a single resource and returns the new resource.<p>
333     *
334     * @param cms the CMS context to use
335     * @param fileName the name of the resource to create
336     * @param targetFolder the folder to store the new resource
337     * @param content the content of the resource to create
338     *
339     * @return the new resource
340     *
341     * @throws CmsException if something goes wrong
342     * @throws CmsLoaderException if something goes wrong
343     * @throws CmsDbSqlException if something goes wrong
344     */
345    @SuppressWarnings("deprecation")
346    private CmsResource createSingleResource(CmsObject cms, String fileName, String targetFolder, byte[] content)
347    throws CmsException, CmsLoaderException, CmsDbSqlException {
348
349        String folderRootPath = cms.getRequestContext().addSiteRoot(targetFolder);
350        if (!m_uploadRestrictionInfo.isUploadEnabled(folderRootPath)) {
351            LOG.error("Upload not enabled for folder " + targetFolder);
352            return null;
353        }
354
355        String newResname = getNewResourceName(cms, fileName, targetFolder, isKeepFileNames());
356        CmsResource createdResource = null;
357
358        // determine Title property value to set on new resource
359        String title = fileName;
360        if (title.lastIndexOf('.') != -1) {
361            title = title.substring(0, title.lastIndexOf('.'));
362        }
363
364        // fileName really shouldn't contain the full path, but for some reason it does sometimes when the client is
365        // running on IE7, so we eliminate anything before and including the last slash or backslash in the title
366        // before setting it as a property.
367
368        int backslashIndex = title.lastIndexOf('\\');
369        if (backslashIndex != -1) {
370            title = title.substring(backslashIndex + 1);
371        }
372
373        int slashIndex = title.lastIndexOf('/');
374        if (slashIndex != -1) {
375            title = title.substring(slashIndex + 1);
376        }
377
378        List<CmsProperty> properties = new ArrayList<CmsProperty>(1);
379        CmsProperty titleProp = new CmsProperty();
380        titleProp.setName(CmsPropertyDefinition.PROPERTY_TITLE);
381        if (OpenCms.getWorkplaceManager().isDefaultPropertiesOnStructure()) {
382            titleProp.setStructureValue(title);
383        } else {
384            titleProp.setResourceValue(title);
385        }
386        properties.add(titleProp);
387
388        int plainId = OpenCms.getResourceManager().getResourceType(
389            CmsResourceTypePlain.getStaticTypeName()).getTypeId();
390        if (!cms.existsResource(newResname, CmsResourceFilter.IGNORE_EXPIRATION)) {
391            // if the resource does not exist, create it
392
393            try {
394                // create the resource
395                int resTypeId = OpenCms.getResourceManager().getDefaultTypeForName(newResname).getTypeId();
396                createdResource = cms.createResource(newResname, resTypeId, content, properties);
397                try {
398                    cms.unlockResource(newResname);
399                } catch (CmsLockException e) {
400                    LOG.info("Couldn't unlock uploaded file", e);
401                }
402            } catch (CmsSecurityException e) {
403                // in case of not enough permissions, try to create a plain text file
404                createdResource = cms.createResource(newResname, plainId, content, properties);
405                cms.unlockResource(newResname);
406            } catch (CmsDbSqlException sqlExc) {
407                // SQL error, probably the file is too large for the database settings, delete file
408                cms.lockResource(newResname);
409                cms.deleteResource(newResname, CmsResource.DELETE_PRESERVE_SIBLINGS);
410                throw sqlExc;
411            } catch (OutOfMemoryError e) {
412                // the file is to large try to clear up
413                cms.lockResource(newResname);
414                cms.deleteResource(newResname, CmsResource.DELETE_PRESERVE_SIBLINGS);
415                throw e;
416            }
417
418        } else {
419            // if the resource already exists, replace it
420            CmsResource res = cms.readResource(newResname, CmsResourceFilter.ALL);
421            boolean wasLocked = false;
422            try {
423                if (!cms.getLock(res).isOwnedBy(cms.getRequestContext().getCurrentUser())) {
424                    cms.lockResource(res);
425                    wasLocked = true;
426                }
427                CmsFile file = cms.readFile(res);
428                byte[] contents = file.getContents();
429                try {
430                    cms.replaceResource(newResname, res.getTypeId(), content, null);
431                    createdResource = res;
432                } catch (CmsDbSqlException sqlExc) {
433                    // SQL error, probably the file is too large for the database settings, restore content
434                    file.setContents(contents);
435                    cms.writeFile(file);
436                    throw sqlExc;
437                } catch (OutOfMemoryError e) {
438                    // the file is to large try to clear up
439                    file.setContents(contents);
440                    cms.writeFile(file);
441                    throw e;
442                }
443            } finally {
444                if (wasLocked) {
445                    cms.unlockResource(res);
446                }
447            }
448        }
449        return createdResource;
450    }
451
452    /**
453     * Creates the upload target folder.
454     *
455     * @param cms the CMS context
456     * @param targetFolder the upload target folder
457     * @return the new folder
458     * @throws CmsException if something goes wrong
459     */
460    private CmsResource createTargetFolder(CmsObject cms, String targetFolder) throws CmsException {
461
462        List<String> parentFolders = new ArrayList<>();
463        String currentFolder = targetFolder;
464        while ((currentFolder != null) && !cms.existsResource(currentFolder, CmsResourceFilter.IGNORE_EXPIRATION)) {
465            parentFolders.add(currentFolder);
466            currentFolder = CmsResource.getParentFolder(currentFolder);
467        }
468        Collections.reverse(parentFolders);
469        CmsResource lastCreated = null;
470        CmsResource firstCreated = null;
471        for (String parentFolder : parentFolders) {
472
473            CmsResource createdFolder = cms.createResource(
474                parentFolder,
475                OpenCms.getResourceManager().getResourceType(CmsResourceTypeFolder.getStaticTypeName()));
476            lastCreated = createdFolder;
477            if (firstCreated == null) {
478                firstCreated = createdFolder;
479            }
480        }
481        cms.unlockResource(firstCreated);
482        return lastCreated;
483
484    }
485
486    /**
487     * Returns the stacktrace of the given exception as String.<p>
488     *
489     * @param e the exception
490     *
491     * @return the stacktrace as String
492     */
493    private String formatStackTrace(Throwable e) {
494
495        return StringUtils.join(CmsLog.render(e), '\n');
496    }
497
498    /**
499     * Generates a JSON object and returns its String representation for the response.<p>
500     *
501     * @param success <code>true</code> if the upload was successful
502     * @param message the message to display
503     * @param stacktrace the stack trace in case of an error
504     *
505     * @return the the response String
506     */
507    private String generateResponse(Boolean success, String message, String stacktrace) {
508
509        JSONObject result = new JSONObject();
510        try {
511            result.put(I_CmsUploadConstants.KEY_SUCCESS, success);
512            result.put(I_CmsUploadConstants.KEY_MESSAGE, message);
513            result.put(I_CmsUploadConstants.KEY_STACKTRACE, stacktrace);
514            result.put(I_CmsUploadConstants.KEY_REQUEST_SIZE, getRequest().getContentLength());
515
516            // UUIDs get converted to strings when generating the JSON text
517            result.put(I_CmsUploadConstants.KEY_UPLOADED_FILES, new JSONArray(m_resourcesCreated.keySet()));
518
519            result.put(I_CmsUploadConstants.KEY_UPLOADED_FILE_NAMES, new JSONArray(m_resourcesCreated.values()));
520            if (m_uploadHook != null) {
521                result.put(I_CmsUploadConstants.KEY_UPLOAD_HOOK, m_uploadHook);
522            }
523        } catch (JSONException e) {
524            LOG.error(m_bundle.key(org.opencms.ade.upload.Messages.ERR_UPLOAD_JSON_0), e);
525        }
526        return result.toString();
527    }
528
529    /**
530     * Returns the error message if an error occurred during the creation of resources in the VFS.<p>
531     *
532     * @return the error message
533     */
534    private String getCreationErrorMessage() {
535
536        String message = new String();
537        if (!m_resourcesCreated.isEmpty()) {
538            // some resources have been created, tell the user which resources were created successfully
539            StringBuffer buf = new StringBuffer(64);
540            for (String name : m_resourcesCreated.values()) {
541                buf.append("<br />");
542                buf.append(name);
543            }
544            message = m_bundle.key(org.opencms.ade.upload.Messages.ERR_UPLOAD_CREATING_1, buf.toString());
545        } else {
546            // no resources have been created on the VFS
547            message = m_bundle.key(org.opencms.ade.upload.Messages.ERR_UPLOAD_CREATING_0);
548        }
549        return message;
550    }
551
552    /**
553     * Gets the list of file names that should be unziped.<p>
554     *
555     * @return the list of file names that should be unziped
556     *
557     * @throws UnsupportedEncodingException if something goes wrong
558     */
559    private List<String> getFilesToUnzip() throws UnsupportedEncodingException {
560
561        if (m_parameterMap.get(I_CmsUploadConstants.UPLOAD_UNZIP_FILES_FIELD_NAME) != null) {
562            String[] filesToUnzip = m_parameterMap.get(I_CmsUploadConstants.UPLOAD_UNZIP_FILES_FIELD_NAME);
563            if (filesToUnzip != null) {
564                List<String> result = new ArrayList<String>();
565                for (String filename : filesToUnzip) {
566                    result.add(URLDecoder.decode(filename, "UTF-8"));
567                }
568                return result;
569            }
570        }
571        return Collections.emptyList();
572    }
573
574    /**
575     * Gets the post-create handler.
576     *
577     * @return the post-create handler
578     */
579    private String getPostCreateHandler() {
580
581        String[] values = m_parameterMap.get(I_CmsUploadConstants.POST_CREATE_HANDLER);
582        return ((values != null) && (values.length > 0)) ? values[0] : null;
583    }
584
585    /**
586     * Returns the target folder for the new resource,
587     * if the given folder does not exist root folder
588     * of the current site is returned.<p>
589     *
590     * @param cms the CMS context to use
591     *
592     * @return the target folder for the new resource
593     *
594     * @throws CmsException if something goes wrong
595     */
596    private String getTargetFolder(CmsObject cms) throws CmsException {
597
598        // get the target folder on the vfs
599        CmsResource target = cms.readResource("/", CmsResourceFilter.IGNORE_EXPIRATION);
600        if (m_parameterMap.get(I_CmsUploadConstants.UPLOAD_TARGET_FOLDER_FIELD_NAME) != null) {
601            String targetFolder = m_parameterMap.get(I_CmsUploadConstants.UPLOAD_TARGET_FOLDER_FIELD_NAME)[0];
602            if (!CmsStringUtil.isEmptyOrWhitespaceOnly(targetFolder)) {
603                if (cms.existsResource(targetFolder, CmsResourceFilter.IGNORE_EXPIRATION)) {
604                    CmsResource tmpTarget = cms.readResource(targetFolder, CmsResourceFilter.IGNORE_EXPIRATION);
605                    if (tmpTarget.isFolder()) {
606                        target = tmpTarget;
607                    }
608                } else {
609                    target = createTargetFolder(cms, targetFolder);
610                }
611            }
612        }
613        String targetFolder = cms.getRequestContext().removeSiteRoot(target.getRootPath());
614        if (!targetFolder.endsWith("/")) {
615            // add folder separator to currentFolder
616            targetFolder += "/";
617        }
618        return targetFolder;
619    }
620
621    /**
622     * Returns true if file name translation should be skipped for the upload.
623     *
624     * <p>This is mainly used for the file replacement dialog.
625     *
626     * @return true if file name translation should be skipped
627     */
628    private boolean isKeepFileNames() {
629
630        String[] values = m_parameterMap.get(I_CmsUploadConstants.KEEP_FILE_NAMES);
631        boolean result = (values != null) && (values.length > 0) && Boolean.parseBoolean(values[0]);
632        return result;
633    }
634
635    /**
636     * Parses the request.<p>
637     *
638     * Stores the file items and the request parameters in a local variable if present.<p>
639     *
640     * @param listener the upload listener
641     *
642     * @throws Exception if anything goes wrong
643     */
644    private void parseRequest(CmsUploadListener listener) throws Exception {
645
646        // check if the request is a multipart request
647        if (!ServletFileUpload.isMultipartContent(getRequest())) {
648            // no multipart request: Abort the upload
649            throw new CmsUploadException(m_bundle.key(org.opencms.ade.upload.Messages.ERR_UPLOAD_NO_MULTIPART_0));
650        }
651
652        // this was indeed a multipart form request, read the files
653        m_multiPartFileItems = readMultipartFileItems(listener);
654
655        // check if there were any multipart file items in the request
656        if ((m_multiPartFileItems == null) || m_multiPartFileItems.isEmpty()) {
657            // no file items found stop process
658            throw new CmsUploadException(m_bundle.key(org.opencms.ade.upload.Messages.ERR_UPLOAD_NO_FILEITEMS_0));
659        }
660
661        // there are file items in the request, get the request parameters
662        m_parameterMap = CmsRequestUtil.readParameterMapFromMultiPart(
663            getCmsObject().getRequestContext().getEncoding(),
664            m_multiPartFileItems);
665
666        listener.setFinished(true);
667    }
668
669    /**
670     * Parses a request of the form <code>multipart/form-data</code>.<p>
671     *
672     * The result list will contain items of type <code>{@link FileItem}</code>.
673     * If the request has no file items, then <code>null</code> is returned.<p>
674     *
675     * @param listener the upload listener
676     *
677     * @return the list of <code>{@link FileItem}</code> extracted from the multipart request,
678     *      or <code>null</code> if the request has no file items
679     *
680     * @throws Exception if anything goes wrong
681     */
682    private List<FileItem> readMultipartFileItems(CmsUploadListener listener) throws Exception {
683
684        DiskFileItemFactory factory = new DiskFileItemFactory();
685        // maximum size that will be stored in memory
686        factory.setSizeThreshold(4096);
687        // the location for saving data that is larger than the threshold
688        File temp = new File(OpenCms.getSystemInfo().getPackagesRfsPath());
689        if (temp.exists() || temp.mkdirs()) {
690            // make sure the folder exists
691            factory.setRepository(temp);
692        }
693
694        // create a file upload servlet
695        ServletFileUpload fu = new ServletFileUpload(factory);
696        // set the listener
697        fu.setProgressListener(listener);
698        // set encoding to correctly handle special chars (e.g. in filenames)
699        fu.setHeaderEncoding(getRequest().getCharacterEncoding());
700        // set the maximum size for a single file (value is in bytes)
701        long maxFileSizeBytes = OpenCms.getWorkplaceManager().getFileBytesMaxUploadSize(getCmsObject());
702        if (maxFileSizeBytes > 0) {
703            fu.setFileSizeMax(maxFileSizeBytes);
704        }
705
706        // try to parse the request
707        try {
708            return CmsCollectionsGenericWrapper.list(fu.parseRequest(getRequest()));
709        } catch (SizeLimitExceededException e) {
710            // request size is larger than maximum allowed request size, throw an error
711            Integer actualSize = Integer.valueOf((int)(e.getActualSize() / 1024));
712            Integer maxSize = Integer.valueOf((int)(e.getPermittedSize() / 1024));
713            throw new CmsUploadException(
714                m_bundle.key(org.opencms.ade.upload.Messages.ERR_UPLOAD_REQUEST_SIZE_LIMIT_2, actualSize, maxSize),
715                e);
716        } catch (FileSizeLimitExceededException e) {
717            // file size is larger than maximum allowed file size, throw an error
718            Integer actualSize = Integer.valueOf((int)(e.getActualSize() / 1024));
719            Integer maxSize = Integer.valueOf((int)(e.getPermittedSize() / 1024));
720            throw new CmsUploadException(
721                m_bundle.key(
722                    org.opencms.ade.upload.Messages.ERR_UPLOAD_FILE_SIZE_LIMIT_3,
723                    actualSize,
724                    e.getFileName(),
725                    maxSize),
726                e);
727        }
728    }
729
730    /**
731     * Remove the listener active in this session.
732     *
733     * @param listenerId the id of the listener to remove
734     */
735    private void removeListener(CmsUUID listenerId) {
736
737        getRequest().getSession().removeAttribute(SESSION_ATTRIBUTE_LISTENER_ID);
738        m_listeners.remove(listenerId);
739    }
740}