/*
 * File   : $Source: /usr/local/cvs/opencms/src-modules/org/opencms/workplace/tools/database/CmsHtmlImport.java,v $
 * Date   : $Date: 2008-02-27 12:05:51 $
 * Version: $Revision: 1.20 $
 *
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.workplace.tools.database;

import org.opencms.db.CmsDbIoException;
import org.opencms.file.CmsFolder;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsProperty;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.types.CmsResourceTypeFolder;
import org.opencms.file.types.CmsResourceTypeImage;
import org.opencms.file.types.CmsResourceTypePlain;
import org.opencms.file.types.CmsResourceTypePointer;
import org.opencms.file.types.CmsResourceTypeXmlPage;
import org.opencms.i18n.CmsEncoder;
import org.opencms.i18n.CmsMessageContainer;
import org.opencms.importexport.CmsImportExportException;
import org.opencms.loader.CmsResourceManager;
import org.opencms.lock.CmsLock;
import org.opencms.lock.CmsLockType;
import org.opencms.main.CmsException;
import org.opencms.main.CmsIllegalArgumentException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.relations.CmsLink;
import org.opencms.report.I_CmsReport;
import org.opencms.staticexport.CmsLinkTable;
import org.opencms.util.CmsFileUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.xml.page.CmsXmlPage;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.collections.ExtendedProperties;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.logging.Log;

/**
 * This class implements the HTML->OpenCms Template converter for OpenCms 6.x.<p>
 * 
 * The HTML files can lay in a directory or in a zip file. The entries in the zip file 
 * are saved temporary in the tmp-directory of the system. Every file is stored into the 
 * correct location in the OpenCms VFS.<p>
 * 
 * 
 * @author Michael Emmerich 
 * @author Armen Markarian 
 * @author Peter Bonrad
 * @author Anja Röttgers
 * 
 * @version $Revision: 1.20 $ 
 * 
 * @since 6.0.0 
 */
public class CmsHtmlImport {

    /** filename of the meta.properties file. */
    public static final String META_PROPERTIES = "meta.properties";

    /** The log object for this class. */
    private static final Log LOG = CmsLog.getLog(CmsHtmlImport.class);

    /**
     * This function creates a folder in the temporary-directory.<p>
     * 
     * @param name the name of the folder
     * 
     * @return the folder file
     * 
     * @throws Exception if the folder can not create
     */
    public static File createTempFolder(String name) throws Exception {

        File folder = null;
        folder = File.createTempFile(name, "", null);
        folder.delete();
        folder.mkdirs();
        folder.deleteOnExit();
        return folder;
    }

    /** the CmsObject to use. */
    private CmsObject m_cmsObject;

    /** the destination directory in the OpenCms VFS. */
    private String m_destinationDir;

    /** the download gallery name. */
    private String m_downloadGallery;

    /** the element name of the template. */
    private String m_element;

    /** the end pattern for extracting content. */
    private String m_endPattern;

    /** HashMap of all known extensions in OpenCms. */
    private Map m_extensions;

    /** Storage for external links. */
    private HashSet m_externalLinks;

    /** The file index contains all resource names in the real file system and their renamed ones in the OpenCms VFS. */
    private HashMap m_fileIndex;

    /** the HTML converter to parse and modify the content. */
    private CmsHtmlImportConverter m_htmlConverter;

    /** the path of the import temporary file in the "real" file system.*/
    private String m_httpDir;

    /** the image gallery name. */
    private String m_imageGallery;

    /** Storage for image alt tags, it is filled by the HtmlConverter each time a new image is found. */
    private HashMap m_imageInfo;

    /** the input directory in the "real" file system. */
    private String m_inputDir;

    /** the encoding used for all imported input files. */
    private String m_inputEncoding;

    /** should broken links be kept. */
    private boolean m_keepBrokenLinks;

    /** the external link gallery name. */
    private String m_linkGallery;

    /** the local use for content definition. */
    private String m_locale;

    /** the overwrite value new resources. */
    private boolean m_overwrite;

    /** Map with all parents in file system to OpenCms. */
    private HashMap m_parents;

    /** the report for the output. */
    private I_CmsReport m_report;

    /** the start pattern for extracting content. */
    private String m_startPattern;

    /** the template use for all pages. */
    private String m_template;

    /**
     * Default Constructor.<p>
     */
    public CmsHtmlImport() {

        m_overwrite = true;
        m_extensions = OpenCms.getResourceManager().getExtensionMapping();
        m_fileIndex = new HashMap();
        m_parents = new HashMap();
        m_imageInfo = new HashMap();
        m_externalLinks = new HashSet();
        m_htmlConverter = new CmsHtmlImportConverter(this, false);
    }

    /**
     * Creates a new import object for the given cms object.<p>
     * 
     * @param cms the current cms context
     */
    public CmsHtmlImport(CmsObject cms) {

        this();
        m_cmsObject = cms;
    }

    /**
     * Calculates an absolute uri from a relative "uri" and the given absolute "baseUri".<p> 
     * 
     * If "uri" is already absolute, it is returned unchanged.
     * This method also returns "uri" unchanged if it is not well-formed.<p>
     *    
     * @param relativeUri the relative uri to calculate an absolute uri for
     * @param baseUri the base uri, this must be an absolute uri
     * @return an absolute uri calculated from "uri" and "baseUri"
     */
    public String getAbsoluteUri(String relativeUri, String baseUri) {

        if ((relativeUri == null) || (relativeUri.charAt(0) == '/') || (relativeUri.startsWith("#"))) {

            return relativeUri;
        }

        // if we are on a windows system, we must add a ":" in the uri later               
        String windowsAddition = "";
        if (File.separator.equals("\\")) {
            windowsAddition = ":";
        }

        try {
            URL baseUrl = new URL("file://");
            URL url = new URL(new URL(baseUrl, "file://" + baseUri), relativeUri);
            if (url.getQuery() == null) {
                if (url.getRef() == null) {
                    return url.getHost() + windowsAddition + url.getPath();
                } else {
                    return url.getHost() + windowsAddition + url.getPath() + "#" + url.getRef();
                }
            } else {
                return url.getHost() + windowsAddition + url.getPath() + "?" + url.getQuery();
            }
        } catch (MalformedURLException e) {
            return relativeUri;
        }
    }

    /**
     * Returns the destinationDir.<p>
     *
     * @return the destinationDir
     */
    public String getDestinationDir() {

        return m_destinationDir;
    }

    /**
     * Returns the downloadGallery.<p>
     *
     * @return the downloadGallery
     */
    public String getDownloadGallery() {

        return m_downloadGallery;
    }

    /**
     * Returns the element.<p>
     *
     * @return the element
     */
    public String getElement() {

        return m_element;
    }

    /**
     * Returns the endPattern.<p>
     *
     * @return the endPattern
     */
    public String getEndPattern() {

        return m_endPattern;
    }

    /**
     * Returns the httpDir.<p>
     *
     * @return the httpDir
     */
    public String getHttpDir() {

        return m_httpDir;
    }

    /**
     * Returns the imageGallery.<p>
     *
     * @return the imageGallery
     */
    public String getImageGallery() {

        return m_imageGallery;
    }

    /**
     * Returns the inputDir.<p>
     *
     * @return the inputDir
     */
    public String getInputDir() {

        return m_inputDir;
    }

    /**
     * Returns the inputEncoding.<p>
     *
     * @return the inputEncoding
     */
    public String getInputEncoding() {

        return m_inputEncoding;
    }

    /**
     * Returns the linkGallery.<p>
     *
     * @return the linkGallery
     */
    public String getLinkGallery() {

        return m_linkGallery;
    }

    /**
     * Returns the local.<p>
     *
     * @return the local
     */
    public String getLocale() {

        return m_locale;
    }

    /**
     * Returns the startPattern.<p>
     *
     * @return the startPattern
     */
    public String getStartPattern() {

        return m_startPattern;
    }

    /**
     * Returns the template.<p>
     *
     * @return the template
     */
    public String getTemplate() {

        return m_template;
    }

    /**
     * Returns the keepBrokenLinks.<p>
     *
     * @return the keepBrokenLinks
     */
    public boolean isKeepBrokenLinks() {

        return m_keepBrokenLinks;
    }

    /**
     * Returns the overwrite.<p>
     *
     * @return the overwrite
     */
    public boolean isOverwrite() {

        return m_overwrite;
    }

    /**
     * Sets the cmsObject.<p>
     *
     * @param cmsObject the cmsObject to set
     */
    public void setCmsObject(CmsObject cmsObject) {

        m_cmsObject = cmsObject;
    }

    /**
     * Sets the destinationDir.<p>
     *
     * @param destinationDir the destinationDir to set
     */
    public void setDestinationDir(String destinationDir) {

        m_destinationDir = destinationDir;
    }

    /**
     * Sets the downloadGallery.<p>
     *
     * @param downloadGallery the downloadGallery to set
     */
    public void setDownloadGallery(String downloadGallery) {

        m_downloadGallery = downloadGallery;
    }

    /**
     * Sets the element.<p>
     *
     * @param element the element to set
     */
    public void setElement(String element) {

        m_element = element;
    }

    /**
     * Sets the endPattern.<p>
     *
     * @param endPattern the endPattern to set
     */
    public void setEndPattern(String endPattern) {

        m_endPattern = endPattern;
    }

    /**
     * Sets the httpDir.<p>
     *
     * @param httpDir the httpDir to set
     */
    public void setHttpDir(String httpDir) {

        m_httpDir = httpDir;
    }

    /**
     * Sets the imageGallery.<p>
     *
     * @param imageGallery the imageGallery to set
     */
    public void setImageGallery(String imageGallery) {

        m_imageGallery = imageGallery;
    }

    /**
     * Sets the inputDir.<p>
     *
     * @param inputDir the inputDir to set
     */
    public void setInputDir(String inputDir) {

        m_inputDir = inputDir;
    }

    /**
     * Sets the inputEncoding.<p>
     *
     * @param inputEncoding the inputEncoding to set
     */
    public void setInputEncoding(String inputEncoding) {

        m_inputEncoding = inputEncoding;
    }

    /**
     * Sets the keepBrokenLinks.<p>
     *
     * @param keepBrokenLinks the keepBrokenLinks to set
     */
    public void setKeepBrokenLinks(boolean keepBrokenLinks) {

        m_keepBrokenLinks = keepBrokenLinks;
    }

    /**
     * Sets the linkGallery.<p>
     *
     * @param linkGallery the linkGallery to set
     */
    public void setLinkGallery(String linkGallery) {

        m_linkGallery = linkGallery;
    }

    /**
     * Sets the local.<p>
     *
     * @param locale the local to set
     */
    public void setLocale(String locale) {

        m_locale = locale;
    }

    /**
     * Sets the overwrite.<p>
     *
     * @param overwrite the overwrite to set
     */
    public void setOverwrite(boolean overwrite) {

        m_overwrite = overwrite;
    }

    /**
     * Sets the startPattern.<p>
     *
     * @param startPattern the startPattern to set
     */
    public void setStartPattern(String startPattern) {

        m_startPattern = startPattern;
    }

    /**
     * Sets the template.<p>
     *
     * @param template the template to set
     */
    public void setTemplate(String template) {

        m_template = template;
    }

    /**
     * Imports all resources from the real file system, stores them into the correct locations
     * in the OpenCms VFS and modifies all links. This method is called form the JSP to start the
     * import process.<p>
     * 
     * @param report StringBuffer for reporting
     * 
     * @throws Exception if something goes wrong
     */
    public void startImport(I_CmsReport report) throws Exception {

        try {
            m_report = report;
            m_report.println(Messages.get().container(Messages.RPT_HTML_IMPORT_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);

            boolean isStream = !CmsStringUtil.isEmptyOrWhitespaceOnly(m_httpDir);
            File streamFolder = null;
            if (isStream) {
                // the input is starting through the HTTP upload
                streamFolder = unzipStream();
                m_inputDir = streamFolder.getAbsolutePath();
            }

            // first build the index of all resources
            buildIndex(m_inputDir);
            // build list with all parent resources of input directory for links to outside import folder
            buildParentPath();
            // copy and parse all HTML files first. during the copy process we will collect all 
            // required data for downloads and images
            copyHtmlFiles(m_inputDir);
            // now copy the other files
            copyOtherFiles(m_inputDir);
            // finally create all the external links    
            createExternalLinks();

            if (isStream && streamFolder != null) {
                m_report.println(Messages.get().container(Messages.RPT_HTML_DELETE_0), I_CmsReport.FORMAT_NOTE);
                // delete the files of the zip file
                CmsFileUtil.purgeDirectory(streamFolder);
                // deletes the zip file
                File file = new File(m_httpDir);
                if (file.exists() && file.canWrite()) {
                    file.delete();
                }
            }
            m_report.println(Messages.get().container(Messages.RPT_HTML_IMPORT_END_0), I_CmsReport.FORMAT_HEADLINE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Add a new external link to the storage of external links.<p>
     * 
     * All links in this storage are later used to create entries in the external link gallery.<p>
     * 
     * @param externalLink link to an external resource
     * 
     * @return the complete path to the external link file, if one is created.
     */
    public String storeExternalLink(String externalLink) {

        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_linkGallery)) {
            m_externalLinks.add(externalLink);
            return getExternalLinkFile(externalLink);
        }

        return null;
    }

    /**
     * Add a new image info to the storage of image info's.<p>
     * 
     * The image info's are later used to set the description properties of the images.<p>
     * 
     * @param image the name of the image
     * @param altText the alt-text of the image
     */
    public void storeImageInfo(String image, String altText) {

        m_imageInfo.put(image, altText);
    }

    /**
     * Translated a link into the real file system to its new location in the OpenCms VFS.<p>
     * 
     * This is needed by the HtmlConverter to get the correct links for link translation.<p>
     * 
     * @param link link to the real file system
     * 
     * @return string containing absolute link into the OpenCms VFS
     */
    public String translateLink(String link) {

        String translatedLink = null;
        translatedLink = (String)m_fileIndex.get(link.replace('\\', '/'));

        if (translatedLink == null) {
            // its an anchor link, so copy use it
            if (link.startsWith("#")) {
                translatedLink = link;
            }

            // relative link to OpenCms root
            else if (link.startsWith("/")) {

                // strip cms context path
                if (link.startsWith(OpenCms.getSystemInfo().getOpenCmsContext())) {
                    link = link.substring(OpenCms.getSystemInfo().getOpenCmsContext().length());
                }

                // check if resource exists
                if ((m_keepBrokenLinks) || (m_cmsObject.existsResource(link))) {
                    translatedLink = link;
                }
            }

            else {

                String fileBase = getBasePath(m_inputDir, link);
                String cmsBase = (String)m_parents.get(fileBase);
                if (cmsBase != null) {
                    String outLink = cmsBase + link.substring(fileBase.length()).replace('\\', '/');
                    if ((m_keepBrokenLinks) || (m_cmsObject.existsResource(outLink))) {
                        translatedLink = outLink;
                    }
                }
            }
        }

        // if the link goes to a directory, lets link to the index page within
        if ((translatedLink != null) && translatedLink.endsWith("/")) {
            translatedLink += "index.html";
        }

        // final check: if the translated link is still null the original link found
        // was broken
        // lets link it to the same page, the link is found on
        if (translatedLink == null) {
            translatedLink = "#";
        }

        return translatedLink;
    }

    /**
     * Tests if all given input parameters for the HTML Import are valid, that is that all the 
     * given folders do exist. <p>
     * 
     * @param fi a file item if a file is uploaded per HTTP otherwise <code>null</code>
     * @param isdefault if this sets, then the destination and input directory can be empty
     * 
     * @throws CmsIllegalArgumentException if some parameters are not valid
     */
    public void validate(FileItem fi, boolean isdefault) throws CmsIllegalArgumentException {

        // check the input directory and the HTTP upload file
        if (fi == null) {

            if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_inputDir) && !isdefault) {
                throw new CmsIllegalArgumentException(Messages.get().container(
                    Messages.GUI_HTMLIMPORT_INPUTDIR_1,
                    m_inputDir));
            } else if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_inputDir)) {

                File inputDir = new File(m_inputDir);
                if (!inputDir.exists() || inputDir.isFile()) {
                    throw new CmsIllegalArgumentException(Messages.get().container(
                        Messages.GUI_HTMLIMPORT_INPUTDIR_1,
                        m_inputDir));
                }
            }
        }

        // check the destination directory        
        try {
            if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_destinationDir) && !isdefault) {
                throw new CmsIllegalArgumentException(Messages.get().container(
                    Messages.GUI_HTMLIMPORT_DESTDIR_1,
                    m_destinationDir));
            } else if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_destinationDir)) {
                m_cmsObject.readFolder(m_destinationDir);
            }

        } catch (CmsException e) {
            // an exception is thrown if the folder does not exist
            throw new CmsIllegalArgumentException(Messages.get().container(
                Messages.GUI_HTMLIMPORT_DESTDIR_1,
                m_destinationDir), e);
        }

        // check the image gallery
        // only if flag for leaving images at original location is off
        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_imageGallery)) {
            try {
                CmsFolder folder = m_cmsObject.readFolder(m_imageGallery);
                // check if folder is a image gallery
                String name = OpenCms.getResourceManager().getResourceType(folder.getTypeId()).getTypeName();
                if (!name.equals("imagegallery")) {
                    throw new CmsIllegalArgumentException(Messages.get().container(
                        Messages.GUI_HTMLIMPORT_IMGGALLERY_INVALID_1,
                        m_imageGallery));
                }
            } catch (CmsException e) {
                // an exception is thrown if the folder does not exist
                throw new CmsIllegalArgumentException(Messages.get().container(
                    Messages.GUI_HTMLIMPORT_IMGGALLERY_1,
                    m_imageGallery), e);
            }
        }

        // check the link gallery
        // only if flag for leaving external links at original location is off
        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_linkGallery)) {
            try {
                CmsFolder folder = m_cmsObject.readFolder(m_linkGallery);
                // check if folder is a link gallery
                String name = OpenCms.getResourceManager().getResourceType(folder.getTypeId()).getTypeName();
                if (!name.equals("linkgallery")) {
                    throw new CmsIllegalArgumentException(Messages.get().container(
                        Messages.GUI_HTMLIMPORT_LINKGALLERY_INVALID_1,
                        m_linkGallery));
                }
            } catch (CmsException e) {
                // an exception is thrown if the folder does not exist
                throw new CmsIllegalArgumentException(Messages.get().container(
                    Messages.GUI_HTMLIMPORT_LINKGALLERY_1,
                    m_linkGallery), e);
            }
        }

        // check the download gallery
        if ((!isExternal(m_downloadGallery)) && (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_downloadGallery))) {
            try {
                CmsFolder folder = m_cmsObject.readFolder(m_downloadGallery);
                // check if folder is a download gallery
                String name = OpenCms.getResourceManager().getResourceType(folder.getTypeId()).getTypeName();
                if (!name.equals("downloadgallery")) {
                    throw new CmsIllegalArgumentException(Messages.get().container(
                        Messages.GUI_HTMLIMPORT_DOWNGALLERY_INVALID_1,
                        m_downloadGallery));
                }
            } catch (CmsException e) {
                // an exception is thrown if the folder does not exist
                throw new CmsIllegalArgumentException(Messages.get().container(
                    Messages.GUI_HTMLIMPORT_DOWNGALLERY_1,
                    m_downloadGallery), e);
            }
        }

        // check the template
        try {
            m_cmsObject.readResource(m_template, CmsResourceFilter.ALL);
        } catch (CmsException e) {
            // an exception is thrown if the template does not exist
            if (!isValidElement()) {
                throw new CmsIllegalArgumentException(Messages.get().container(
                    Messages.GUI_HTMLIMPORT_TEMPLATE_1,
                    m_template), e);
            }
        }

        // check the element
        if (!isValidElement()) {
            throw new CmsIllegalArgumentException(Messages.get().container(
                Messages.GUI_HTMLIMPORT_INVALID_ELEM_2,
                m_element,
                m_template));
        }

        // check if we are in an offline project
        if (m_cmsObject.getRequestContext().currentProject().isOnlineProject()) {
            throw new CmsIllegalArgumentException(
                Messages.get().container(Messages.GUI_HTMLIMPORT_CONSTRAINT_OFFLINE_0));
        }
    }

    /**
     * Builds an index of all files to be imported and determines their new names in the OpenCms.<p>
     * 
     * @param startfolder the folder to start with
     * 
     * @throws Exception if something goes wrong
     */
    private void buildIndex(String startfolder) throws Exception {

        File folder = new File(startfolder);
        // get all subresources

        File[] subresources = folder.listFiles();
        // now loop through all subresources and add them to the index list
        for (int i = 0; i < subresources.length; i++) {
            try {

                String relativeFSName = subresources[i].getAbsolutePath().substring(m_inputDir.length() + 1);
                String absoluteVFSName = getVfsName(relativeFSName, subresources[i].getName(), subresources[i].isFile());
                m_report.print(Messages.get().container(Messages.RPT_CREATE_INDEX_0), I_CmsReport.FORMAT_NOTE);
                m_report.print(org.opencms.report.Messages.get().container(
                    org.opencms.report.Messages.RPT_ARGUMENT_1,
                    relativeFSName.replace('\\', '/')));
                m_report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
                m_report.print(Messages.get().container(Messages.RPT_ARROW_RIGHT_0), I_CmsReport.FORMAT_NOTE);
                m_report.print(org.opencms.report.Messages.get().container(
                    org.opencms.report.Messages.RPT_ARGUMENT_1,
                    absoluteVFSName));
                m_report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
                m_fileIndex.put(subresources[i].getAbsolutePath().replace('\\', '/'), absoluteVFSName);
                // if the subresource is a folder, get all subresources of it as well
                if (subresources[i].isDirectory()) {
                    buildIndex(subresources[i].getAbsolutePath());
                }
                m_report.println(
                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
                    I_CmsReport.FORMAT_OK);
            } catch (Exception e) {
                LOG.error(e.getLocalizedMessage(), e);
                m_report.println(e);
            }
        }
    }

    /**
     * Builds a map with all parents of the destination directory to the real file system.<p>
     * So links to resources of outside the import folder can be found.<p>
     */
    private void buildParentPath() {

        String destFolder = m_destinationDir;
        String inputDir = m_inputDir.replace('\\', '/');
        if (!inputDir.endsWith("/")) {
            inputDir += "/";
        }
        int pos = inputDir.lastIndexOf("/");
        while ((pos > 0) && (destFolder != null)) {
            inputDir = inputDir.substring(0, pos);
            m_parents.put(inputDir + "/", destFolder);

            pos = inputDir.lastIndexOf("/", pos - 1);
            destFolder = CmsResource.getParentFolder(destFolder);
        }
    }

    /**
     * This function close a InputStream.<p>
     * 
     * @param stream the <code> {@link InputStream} </code> Object
     */
    private void closeStream(InputStream stream) {

        if (stream != null) {
            try {
                stream.close();
            } catch (Exception ex) {
                LOG.error(ex.getLocalizedMessage(), ex);
            }
        }
    }

    /**
     * Copies all  HTML files to the VFS.<p>
     * 
     * @param startfolder the folder to start with
     * 
     * @throws Exception if something goes wrong
     */
    private void copyHtmlFiles(String startfolder) throws Exception {

        try {
            File folder = new File(startfolder);
            // get all subresources
            File[] subresources = folder.listFiles();
            // now loop through all subresources 
            for (int i = 0; i < subresources.length; i++) {
                // if the subresource is a folder, get all subresources of it as well          
                if (subresources[i].isDirectory()) {
                    // first, create the folder in the VFS    
                    Hashtable properties = new Hashtable();
                    createFolder(subresources[i].getAbsolutePath(), i, properties);
                    // now process all rescources inside of the folder
                    copyHtmlFiles(subresources[i].getAbsolutePath());
                } else {
                    // create a new file in the VFS      
                    String vfsFileName = (String)m_fileIndex.get(subresources[i].getAbsolutePath().replace('\\', '/'));
                    // check if this is an HTML file, do only import and parse those
                    int type = getFileType(vfsFileName);
                    if (CmsResourceTypePlain.getStaticTypeId() == type) {
                        Hashtable properties = new Hashtable();
                        // the subresource is a file, so start the parsing process
                        String content = "";
                        try {
                            content = parseHtmlFile(subresources[i], properties);
                        } catch (CmsException e) {
                            m_report.println(e);
                        }
                        properties.put("template", m_template);

                        // create the file in the VFS
                        createFile(subresources[i].getAbsolutePath(), i, content, properties);
                    }
                }
            }
        } catch (Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * Copies all files except HTML files to the VFS.<p>
     * 
     * @param startfolder the folder to start with
     */
    private void copyOtherFiles(String startfolder) {

        try {
            File folder = new File(startfolder);
            // get all subresources
            File[] subresources = folder.listFiles();
            // now loop through all subresources 
            for (int i = 0; i < subresources.length; i++) {
                // if the subresource is a folder, get all subresources of it as well
                if (subresources[i].isDirectory()) {
                    copyOtherFiles(subresources[i].getAbsolutePath());
                } else {
                    // do not import the "meta.properties" file
                    if (!subresources[i].getName().equals(META_PROPERTIES)) {
                        // create a new file in the VFS      
                        String vfsFileName = (String)m_fileIndex.get(subresources[i].getAbsolutePath().replace(
                            '\\',
                            '/'));
                        // get the file type of the FS file
                        int type = getFileType(vfsFileName);
                        if (CmsResourceTypePlain.getStaticTypeId() != type) {

                            if (isExternal(vfsFileName)) {

                                m_report.print(
                                    Messages.get().container(Messages.RPT_SKIP_EXTERNAL_0),
                                    I_CmsReport.FORMAT_NOTE);
                                m_report.print(org.opencms.report.Messages.get().container(
                                    org.opencms.report.Messages.RPT_ARGUMENT_1,
                                    subresources[i]));
                                m_report.print(org.opencms.report.Messages.get().container(
                                    org.opencms.report.Messages.RPT_DOTS_0));
                                m_report.print(
                                    Messages.get().container(Messages.RPT_ARROW_RIGHT_0),
                                    I_CmsReport.FORMAT_NOTE);
                                m_report.println(org.opencms.report.Messages.get().container(
                                    org.opencms.report.Messages.RPT_ARGUMENT_1,
                                    vfsFileName));
                            } else {

                                m_report.print(Messages.get().container(Messages.RPT_IMPORT_0), I_CmsReport.FORMAT_NOTE);
                                m_report.print(org.opencms.report.Messages.get().container(
                                    org.opencms.report.Messages.RPT_ARGUMENT_1,
                                    vfsFileName));
                                m_report.print(org.opencms.report.Messages.get().container(
                                    org.opencms.report.Messages.RPT_DOTS_0));

                                // get the content of the FS file
                                byte[] content = getFileBytes(subresources[i]);
                                // get the filename from the fileIndex list

                                // check if there are some image info's stored for this resource
                                List properties = new ArrayList();
                                String altText = (String)m_imageInfo.get(subresources[i].getAbsolutePath().replace(
                                    '\\',
                                    '/'));
                                CmsProperty property1 = new CmsProperty(
                                    CmsPropertyDefinition.PROPERTY_DESCRIPTION,
                                    altText,
                                    altText);
                                CmsProperty property2 = new CmsProperty(
                                    CmsPropertyDefinition.PROPERTY_TITLE,
                                    altText,
                                    altText);
                                // add them to the title and description property
                                if (altText != null) {
                                    properties.add(property1);
                                    properties.add(property2);
                                }
                                // create the file
                                if (!m_overwrite) {
                                    m_cmsObject.createResource(vfsFileName, type, content, properties);
                                } else {
                                    try {
                                        CmsLock lock = m_cmsObject.getLock(vfsFileName);
                                        if (lock.getType() != CmsLockType.EXCLUSIVE) {
                                            m_cmsObject.lockResource(vfsFileName);
                                        }
                                        m_cmsObject.deleteResource(vfsFileName, CmsResource.DELETE_PRESERVE_SIBLINGS);
                                    } catch (CmsException e) {
                                        // the file did not exist, so create it                                     
                                    } finally {
                                        m_cmsObject.createResource(vfsFileName, type, content, properties);
                                    }

                                    m_report.print(
                                        Messages.get().container(Messages.RPT_OVERWRITE_0),
                                        I_CmsReport.FORMAT_NOTE);
                                    m_report.print(org.opencms.report.Messages.get().container(
                                        org.opencms.report.Messages.RPT_DOTS_0));
                                }
                                m_report.println(org.opencms.report.Messages.get().container(
                                    org.opencms.report.Messages.RPT_OK_0), I_CmsReport.FORMAT_OK);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
            m_report.println(e);
        }
    }

    /**
     * Creates all external links, which were found during the HTML-page processing.<p>
     * 
     */
    private void createExternalLinks() {

        // loop through all links
        Iterator i = m_externalLinks.iterator();
        while (i.hasNext()) {
            String linkUrl = (String)i.next();
            String filename = getExternalLinkFile(linkUrl);

            m_report.print(Messages.get().container(Messages.RPT_CREATE_EXTERNAL_LINK_0), I_CmsReport.FORMAT_NOTE);
            m_report.print(org.opencms.report.Messages.get().container(
                org.opencms.report.Messages.RPT_ARGUMENT_1,
                filename));
            m_report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));

            List properties = new ArrayList();
            CmsProperty property1 = new CmsProperty(
                CmsPropertyDefinition.PROPERTY_TITLE,
                "Link to " + linkUrl,
                "Link to " + linkUrl);
            properties.add(property1);
            try {
                m_cmsObject.createResource(
                    m_linkGallery + filename,
                    CmsResourceTypePointer.getStaticTypeId(),
                    linkUrl.getBytes(),
                    properties);
            } catch (CmsException e) {
                // do nothing here, an exception will be thrown if this link already exists                
            }
            m_report.println(
                org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
                I_CmsReport.FORMAT_OK);
        }
    }

    /**
     * Creates a file in the VFS.<p>
     * 
     * @param filename the complete filename in the real file system
     * @param position the default navigation position of this folder
     * @param content the HTML content of the file
     * @param properties the file properties
     */
    private void createFile(String filename, int position, String content, Hashtable properties) {

        String vfsFileName = (String)m_fileIndex.get(filename.replace('\\', '/'));

        if (vfsFileName != null) {
            try {

                m_report.print(Messages.get().container(Messages.RPT_CREATE_FILE_0), I_CmsReport.FORMAT_NOTE);
                m_report.print(org.opencms.report.Messages.get().container(
                    org.opencms.report.Messages.RPT_ARGUMENT_1,
                    vfsFileName));
                m_report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));

                // check if we have to set the navpos property.
                if ((properties.get(CmsPropertyDefinition.PROPERTY_NAVPOS) == null)
                    && (properties.get(CmsPropertyDefinition.PROPERTY_NAVTEXT) != null)) {
                    // set the position in the folder as navpos
                    // we have to add one to the position, since it is counted from 0
                    properties.put(CmsPropertyDefinition.PROPERTY_NAVPOS, (position + 1) + "");
                }

                // create new XML page
                Locale locale = new Locale(m_locale);
                CmsXmlPage page = new CmsXmlPage(locale, OpenCms.getSystemInfo().getDefaultEncoding());
                page.addValue(m_element, locale);
                page.setStringValue(m_cmsObject, m_element, locale, content);

                // check links
                CmsLinkTable linkTable = page.getLinkTable(m_element, locale);
                Iterator i = linkTable.iterator();
                while (i.hasNext()) {
                    CmsLink link = (CmsLink)i.next();
                    String target = link.getTarget();
                    // do only update internal links 
                    if (link.isInternal()) {
                        target = m_cmsObject.getRequestContext().getFileTranslator().translateResource(target);
                        // update link
                        link.updateLink(target, link.getAnchor(), link.getQuery());
                        link.checkConsistency(m_cmsObject);
                    }
                }
                // marshal XML page and get the content
                byte[] contentByteArray = page.marshal();
                List oldProperties = new ArrayList();

                if (!m_overwrite) {
                    m_cmsObject.createResource(
                        vfsFileName,
                        CmsResourceTypeXmlPage.getStaticTypeId(),
                        contentByteArray,
                        new ArrayList());
                } else {
                    try {
                        // try if the file is there
                        oldProperties = m_cmsObject.readPropertyObjects(vfsFileName, false);
                        CmsLock lock = m_cmsObject.getLock(vfsFileName);
                        if (lock.getType() != CmsLockType.EXCLUSIVE) {
                            m_cmsObject.lockResource(vfsFileName);
                        }
                        m_cmsObject.deleteResource(vfsFileName, CmsResource.DELETE_PRESERVE_SIBLINGS);
                    } catch (CmsException e) {
                        // the file did not exist, so we do not have to delete it                      
                    } finally {
                        // create the new resource
                        m_report.print(Messages.get().container(Messages.RPT_OVERWRITE_0), I_CmsReport.FORMAT_NOTE);
                        m_report.print(org.opencms.report.Messages.get().container(
                            org.opencms.report.Messages.RPT_DOTS_0));
                        m_cmsObject.createResource(
                            vfsFileName,
                            CmsResourceTypeXmlPage.getStaticTypeId(),
                            contentByteArray,
                            new ArrayList());
                    }
                }
                // create all properties and put them in an ArrayList
                Iterator it = properties.entrySet().iterator();
                List propertyList = new ArrayList();
                while (it.hasNext()) {
                    // get property and value
                    Map.Entry entry = (Map.Entry)it.next();
                    String propertyKey = (String)entry.getKey();
                    String propertyVal = (String)entry.getValue();
                    // create new Property Object
                    CmsProperty property = new CmsProperty(propertyKey, propertyVal, propertyVal);
                    // create implicitly if Property doesn't exist already
                    property.setAutoCreatePropertyDefinition(true);
                    // add new property to the list
                    propertyList.add(property);
                }
                // try to write the properties
                try {
                    m_cmsObject.writePropertyObjects(vfsFileName, propertyList);
                    // write the old properties if available
                    m_cmsObject.writePropertyObjects(vfsFileName, oldProperties);
                } catch (CmsException e1) {
                    e1.printStackTrace();
                }
                m_report.println(
                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
                    I_CmsReport.FORMAT_OK);
            } catch (CmsException e) {
                m_report.println(e);
                LOG.error(e.getLocalizedMessage(), e);
            }
        }
    }

    /**
     * Creates a folder in the VFS.<p>
     * 
     * @param foldername the complete folder name in the real file system
     * @param position the default navigation position of this folder
     * @param properties the file properties
     */
    private void createFolder(String foldername, int position, Hashtable properties) {

        String vfsFolderName = (String)m_fileIndex.get(foldername.replace('\\', '/'));

        m_report.print(Messages.get().container(Messages.RPT_CREATE_FOLDER_0), I_CmsReport.FORMAT_NOTE);
        m_report.print(org.opencms.report.Messages.get().container(
            org.opencms.report.Messages.RPT_ARGUMENT_1,
            vfsFolderName));
        m_report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));

        if (vfsFolderName != null) {
            String path = vfsFolderName.substring(
                0,
                vfsFolderName.substring(0, vfsFolderName.length() - 1).lastIndexOf("/"));
            String folder = vfsFolderName.substring(path.length(), vfsFolderName.length());
            try {
                // try to find a meta.properties file in the folder
                String propertyFileName = foldername + File.separator + META_PROPERTIES;

                boolean metaPropertiesFound = false;
                ExtendedProperties propertyFile = new ExtendedProperties();
                try {
                    propertyFile.load(new FileInputStream(new File(propertyFileName)));
                    metaPropertiesFound = true;
                } catch (Exception e1) {
                    // do nothing if the property file could not be loaded since it is not required
                    // that such s file does exist
                }
                // now copy all values from the property file to the already found properties of the
                // new folder in OpenCms
                // only do this if we have found a meta.properties file          
                if (metaPropertiesFound) {
                    Enumeration enu = propertyFile.keys();
                    String property = "";
                    while (enu.hasMoreElements()) {
                        // get property and value
                        try {
                            property = (String)enu.nextElement();
                            String propertyvalue = (String)propertyFile.get(property);
                            // copy to the properties of the OpenCms folder
                            properties.put(property, propertyvalue);
                        } catch (Exception e2) {
                            // just skip this property if it could not be read.
                            e2.printStackTrace();
                        }
                    }

                    // check if we have to set the navpos property.
                    if (properties.get(CmsPropertyDefinition.PROPERTY_NAVPOS) == null) {
                        // set the position in the folder as navpos
                        // we have to add one to the position, since it is counted from 0
                        properties.put(CmsPropertyDefinition.PROPERTY_NAVPOS, (position + 1) + "");
                    }
                    // check if we have to set the navpos property.
                    if (properties.get(CmsPropertyDefinition.PROPERTY_NAVTEXT) == null) {
                        // set the foldername in the folder as navtext
                        String navtext = folder.substring(1, 2).toUpperCase()
                            + folder.substring(2, folder.length() - 1);
                        properties.put(CmsPropertyDefinition.PROPERTY_NAVTEXT, navtext);
                    }
                } else {
                    // if there was no meta.properties file, no properties should be added to the
                    // folder
                    properties = new Hashtable();
                }
                // try to read the folder, it its there we must not create it again
                try {
                    m_cmsObject.readFolder(path + folder);
                    m_cmsObject.lockResource(path + folder);
                } catch (CmsException e1) {
                    // the folder was not there, so create it
                    m_cmsObject.createResource(path + folder, CmsResourceTypeFolder.getStaticTypeId());
                }
                // create all properties and put them in an ArrayList
                Enumeration enu = properties.keys();
                List propertyList = new ArrayList();
                while (enu.hasMoreElements()) {
                    // get property and value
                    String propertyKey = (String)enu.nextElement();
                    String propertyVal = (String)properties.get(propertyKey);
                    CmsProperty property = new CmsProperty(propertyKey, propertyVal, propertyVal);
                    // create implicitly if Property doesn't exist already
                    property.setAutoCreatePropertyDefinition(true);
                    // add new property to the list
                    propertyList.add(property);
                }
                // try to write the property Objects
                try {
                    m_cmsObject.writePropertyObjects(path + folder, propertyList);
                } catch (CmsException e1) {
                    e1.printStackTrace();
                }
                m_report.println(
                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
                    I_CmsReport.FORMAT_OK);
            } catch (CmsException e) {
                m_report.println(e);
                LOG.error(e.getLocalizedMessage(), e);
            }
        }
    }

    /**
     * Compares two path's for the base part which have both equal.<p>
     * 
     * @param path1 the first path to compare
     * @param path2 the second path to compare
     * 
     * @return the base path of both which are equal
     */
    private String getBasePath(String path1, String path2) {

        StringBuffer base = new StringBuffer();
        path1 = path1.replace('\\', '/');
        path2 = path2.replace('\\', '/');

        String[] parts1 = path1.split("/");
        String[] parts2 = path2.split("/");

        for (int i = 0; i < parts1.length; i++) {
            if (i >= parts2.length) {
                break;
            }
            if (parts1[i].equals(parts2[i])) {
                base.append(parts1[i] + "/");
            }
        }

        return base.toString();
    }

    /**
     * Creates the filename of the file of the external link.<p>
     * 
     * @param link the link to get the file path for.
     * 
     * @return the filename of the file for the external link.
     */
    private String getExternalLinkFile(String link) {

        String filename = link.substring(link.indexOf("://") + 3, link.length());
        filename = m_cmsObject.getRequestContext().getFileTranslator().translateResource(filename.replace('/', '-'));
        return filename;
    }

    /**
     * Returns a byte array containing the content of server FS file.<p>
     *
     * @param file the name of the file to read
     * 
     * @return bytes[] the content of the file
     * 
     * @throws CmsException if something goes wrong
     */
    private byte[] getFileBytes(File file) throws CmsException {

        byte[] buffer = null;

        FileInputStream fileStream = null;
        int charsRead;
        int size;
        try {
            fileStream = new FileInputStream(file);
            charsRead = 0;
            size = new Long(file.length()).intValue();
            buffer = new byte[size];
            while (charsRead < size) {
                charsRead += fileStream.read(buffer, charsRead, size - charsRead);
            }
            return buffer;
        } catch (IOException e) {
            throw new CmsDbIoException(
                Messages.get().container(Messages.ERR_GET_FILE_BYTES_1, file.getAbsolutePath()),
                e);
        } finally {
            closeStream(fileStream);
        }
    }

    /**
     * Returns the OpenCms file type of a real file system file. <p>
     * This is made by checking the extension.<p>
     * 
     * @param filename the name of the file in the real file system  
     * 
     * @return the id of the OpenCms file type
     * 
     * @throws Exception if something goes wrong
     */
    private int getFileType(String filename) throws Exception {

        String extension = "";
        if (filename.indexOf(".") > -1) {
            extension = filename.substring((filename.lastIndexOf(".")));
        }

        String typename = (String)m_extensions.get(extension.toLowerCase());
        if (typename == null) {
            typename = "binary";
        }
        CmsResourceManager resourceManager = OpenCms.getResourceManager();

        return resourceManager.getResourceType(typename).getTypeId();
    }

    /**
     * Gets a valid VfsName form a given name in the real file system.<p>
     * 
     * This name will later be used for all link translations during the HTML-parsing process.<p>
     * 
     * @param relativeName the name in the real file system, relative to the start folder
     * @param name the name of the file
     * @param isFile flag to indicate that the resource is a file
     * 
     * @return a valid name in the VFS
     * 
     * @throws Exception if something goes wrong
     */
    private String getVfsName(String relativeName, String name, boolean isFile) throws Exception {

        // first translate all file-separators to the valid "/" in OpenCms
        String vfsName = relativeName.replace('\\', '/');
        // the resource is a file
        if (isFile) {
            // we must check if it might be copied into a gallery. this can be done by checking the
            // file extension
            int filetype = getFileType(name);

            // there is no name before the ".extension"
            if (name.indexOf(".") == 0) {
                name = "unknown" + name;
                int dot = relativeName.lastIndexOf(".");

                relativeName = relativeName.substring(0, dot) + name;
            }

            // depending on the file-type, the resource must be moved into a special folder in 
            // OpenCms:
            // images -> move into image gallery, if flag to leave at original location is off
            // binary -> move into download gallery, if flag to leave at original location is off
            // plain -> move into destination folder
            // other -> move into download gallery, if flag to leave at original location is off
            boolean leaveImages = CmsStringUtil.isEmptyOrWhitespaceOnly(m_imageGallery);
            boolean leaveDownload = CmsStringUtil.isEmptyOrWhitespaceOnly(m_downloadGallery);
            if ((CmsResourceTypeImage.getStaticTypeId() == filetype) && (!leaveImages)) {
                // move to image gallery
                // as the image gallery is "flat", we must use the file name and not the complete
                // relative name
                vfsName = m_imageGallery + name;
            } else if ((CmsResourceTypePlain.getStaticTypeId() == filetype) || (leaveImages) || (leaveDownload)) {
                // move to destination folder
                //vfsName=m_destinationDir+relativeName;

                // we have to check if there is a folder with the same name but without extension
                // if so, we will move the file into the folder and name it "index.html"
                String folderName = relativeName;
                if (folderName.indexOf(".") > 0) {
                    folderName = folderName.substring(0, folderName.indexOf("."));
                }
                folderName = m_inputDir + "\\" + folderName;
                File folder = new File(folderName);

                if (folder.isDirectory()) {
                    vfsName = m_destinationDir + relativeName.substring(0, relativeName.indexOf(".")) + "/index.html";
                    // System.err.println("MOVING "+ relativeName + " -> " + name.substring(0,name.indexOf("."))+"/index.html");
                } else {
                    // move to destination folder
                    vfsName = m_destinationDir + relativeName;
                }

            } else {
                // everything else will be moved to the download gallery.
                // as the download gallery is "flat", we must use the file name and not the complete
                // relative name
                vfsName = m_downloadGallery + name;
            }
            // now we have the filename in the VFS. its possible that a file with the same name
            // is already existing, in this case, we have to adjust the filename.        
            return validateFilename(vfsName);
        } else {
            // folders are always moved to the destination folder
            vfsName = m_destinationDir + vfsName + "/";
            return vfsName;
        }
    }

    /**
     * Tests if a filename is an external name, that is this name does not point into the OpenCms VFS.<p>
     * A filename is an external name if it contains the string "://", e.g. "http://" or "ftp://".<p>
     * 
     * @param filename the filename to test 
     * 
     * @return true or false
     */
    private boolean isExternal(String filename) {

        boolean external = false;
        if (filename.indexOf("://") > 0) {
            external = true;
        }
        return external;
    }

    /** 
     * Checks if m_element is valid element.<p>
     * 
     * @return true if element is valid, otherwise false
     */
    private boolean isValidElement() {

        boolean validElement = false;
        List elementList = new ArrayList();
        try {
            // get Elements of template stored in Property "template-elements"
            String elements = m_cmsObject.readPropertyObject(
                m_template,
                CmsPropertyDefinition.PROPERTY_TEMPLATE_ELEMENTS,
                false).getValue();
            // template may contain more than one Element
            // Elements are separated by the delimiter ","
            if (elements != null) {
                StringTokenizer T = new StringTokenizer(elements, ",");
                while (T.hasMoreTokens()) {
                    // current element probably looks like "body*|Body" <name><mandatory>|<nicename>
                    String currentElement = T.nextToken();
                    int sepIndex = currentElement.indexOf("|");
                    if (sepIndex != -1) {
                        // current element == "body*"
                        currentElement = currentElement.substring(0, sepIndex);
                    }
                    if (currentElement.endsWith("*")) {
                        // current element == "body"
                        currentElement = currentElement.substring(0, currentElement.length() - 1);
                    }
                    elementList.add(currentElement);
                }
            }
            if (elementList.contains(m_element)) {
                validElement = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return validElement;
    }

    /**
     * Reads the content of an HTML file from the real file system and parses it for link
     * transformation.<p>
     * 
     * @param  file the file in the real file system
     * @param properties the file properties
     * 
     * @return the modified HTML code of the file
     * 
     * @throws CmsException if something goes wrong
     */
    private String parseHtmlFile(File file, Hashtable properties) throws CmsException {

        String parsedHtml = "";
        try {

            byte[] content = getFileBytes(file);

            // use the correct encoding to get the string from the file bytes
            String contentString = new String(content, m_inputEncoding);
            // escape the string to remove all special chars
            contentString = CmsEncoder.escapeNonAscii(contentString);
            // we must substitute all occurrences of "&#", otherwise tidy would remove them
            contentString = CmsStringUtil.substitute(contentString, "&#", "{subst}");
            // parse the content                  
            parsedHtml = m_htmlConverter.convertHTML(
                file.getAbsolutePath(),
                contentString,
                m_startPattern,
                m_endPattern,
                properties);
            // resubstitute the converted HTML code
            parsedHtml = CmsStringUtil.substitute(parsedHtml, "{subst}", "&#");
        } catch (Exception e) {
            CmsMessageContainer message = Messages.get().container(
                Messages.ERR_HTMLIMPORT_PARSE_1,
                file.getAbsolutePath());
            LOG.error(e.getLocalizedMessage(), e);
            throw new CmsImportExportException(message, e);
        }
        return parsedHtml;
    }

    /**
     * This function reads the zip-file and saved the files and directories in a new 
     * temporary-folder.<p>
     * 
     * @return the temporary-folder where the files from the zip-file are saved
     */
    private File unzipStream() {

        ZipInputStream importZip = null;
        File folder = null;
        try {
            // read the zip file
            importZip = new ZipInputStream(new FileInputStream(m_httpDir));
            // create a temporary-folder, where to unzip the zip file
            folder = createTempFolder("import_html");
            ZipEntry entry = null;
            byte[] buffer = null;
            while (true) {
                try {
                    // get the next entry
                    entry = importZip.getNextEntry();
                    if (entry == null) {
                        break;
                    }
                    String name = entry.getName();
                    // make a report for the user
                    m_report.print(Messages.get().container(Messages.RPT_HTML_UNZIP_0), I_CmsReport.FORMAT_NOTE);
                    m_report.print(org.opencms.report.Messages.get().container(
                        org.opencms.report.Messages.RPT_ARGUMENT_1,
                        name));
                    m_report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
                    // replace the VFS separator with the separator of the file system
                    name = name.replace('/', File.separatorChar);
                    String path = folder + File.separator + name;
                    if (entry.isDirectory()) {
                        // create a directory
                        File importFile = new File(path);
                        importFile.mkdirs();
                    } else {
                        // create a file and read the content
                        int size = new Long(entry.getSize()).intValue();
                        if (size == -1) {
                            buffer = CmsFileUtil.readFully(importZip, false);
                        } else {
                            buffer = CmsFileUtil.readFully(importZip, size, false);
                        }
                        // create a new temporary file
                        File importFile = new File(path);
                        File parent = importFile.getParentFile();
                        if (parent != null) {
                            parent.mkdirs();
                        }
                        importFile.createNewFile();
                        // write the content in the file
                        FileOutputStream fileOutput = new FileOutputStream(importFile.getAbsoluteFile());
                        fileOutput.write(buffer);
                        fileOutput.close();
                    }
                    importZip.closeEntry();
                    m_report.println(
                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
                        I_CmsReport.FORMAT_OK);
                } catch (Exception ex) {
                    String name = (entry != null ? entry.getName() : "");
                    if (LOG.isErrorEnabled()) {
                        LOG.error(Messages.get().getBundle().key(Messages.ERR_ZIPFILE_UNZIP_1, name), ex);
                    }
                    m_report.println(
                        Messages.get().container(Messages.ERR_ZIPFILE_UNZIP_1, name),
                        I_CmsReport.FORMAT_ERROR);
                }
                entry = null;
            }
        } catch (Exception ex) {
            if (LOG.isErrorEnabled()) {
                LOG.error(Messages.get().getBundle().key(Messages.ERR_ZIPFILE_READ_1, m_httpDir), ex);
            }
            m_report.println(Messages.get().container(Messages.ERR_ZIPFILE_READ_1, m_httpDir), I_CmsReport.FORMAT_ERROR);
        } finally {
            closeStream(importZip);
        }
        return folder;

    }

    /**
     * Validates a filename for OpenCms.<p>
     * 
     * This method checks if there are any illegal characters in the filename and modifies them
     * if necessary. In addition it ensures that no duplicate filenames are created.<p>
     * 
     * @param filename the filename to validate
     * 
     * @return a validated and unique filename in OpenCms
     */
    private String validateFilename(String filename) {

        // if its an external filename, use it directly        
        if (isExternal(filename)) {
            return filename;
        }

        // check if this resource name does already exist
        // if so add a postfix to the name

        int postfix = 1;
        boolean found = true;
        String validFilename = filename;

        // if we are not in overwrite mode, we must find a valid, non-existing filename
        // otherwise we will use the current translated name
        if (!m_overwrite) {

            while (found) {
                try {
                    // get the translated name, this one only contains valid chars in OpenCms       
                    validFilename = m_cmsObject.getRequestContext().getFileTranslator().translateResource(validFilename);

                    // try to read the file.....
                    found = true;
                    // first try to read it form the fileIndex of already processed files
                    if (!m_fileIndex.containsValue(validFilename.replace('\\', '/'))) {
                        found = false;
                    }
                    if (!found) {
                        found = true;
                        // there was no entry in the fileIndex, so try to read from the VFS         
                        m_cmsObject.readResource(validFilename, CmsResourceFilter.ALL);
                    }
                    // ....it's there, so add a postfix and try again
                    String path = filename.substring(0, filename.lastIndexOf("/") + 1);
                    String name = filename.substring(filename.lastIndexOf("/") + 1, filename.length());
                    validFilename = path;
                    if (name.lastIndexOf(".") > 0) {
                        validFilename += name.substring(0, name.lastIndexOf("."));
                    } else {
                        validFilename += name;
                    }
                    validFilename += "_" + postfix;
                    if (name.lastIndexOf(".") > 0) {
                        validFilename += name.substring(name.lastIndexOf("."), name.length());
                    }
                    postfix++;
                } catch (CmsException e) {
                    // the file does not exist, so we can use this filename                               
                    found = false;
                }
            }

        } else {
            validFilename = validFilename.replace('\\', '/');
        }

        return OpenCms.getResourceManager().getFileTranslator().translateResource(validFilename);
    }

}
