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 GmbH & Co. KG, 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.importexport;
029
030import org.opencms.i18n.CmsEncoder;
031import org.opencms.i18n.CmsMessageContainer;
032import org.opencms.main.CmsLog;
033import org.opencms.main.OpenCms;
034import org.opencms.util.CmsFileUtil;
035import org.opencms.xml.CmsXmlEntityResolver;
036
037import java.io.File;
038import java.io.FileInputStream;
039import java.io.FileNotFoundException;
040import java.io.IOException;
041import java.io.InputStream;
042import java.util.zip.ZipEntry;
043import java.util.zip.ZipException;
044import java.util.zip.ZipFile;
045
046import org.apache.commons.logging.Log;
047
048/**
049 * Import helper.<p>
050 *
051 * @since 7.0.4
052 */
053public class CmsImportHelper {
054
055    /** The log object for this class. */
056    private static final Log LOG = CmsLog.getLog(CmsImport.class);
057
058    /** The folder, or <code>null</code> if a zip file.*/
059    private File m_folder;
060
061    /** The import parameters to use. */
062    private CmsImportParameters m_params;
063
064    /** The zip file, or <code>null</code> if a folder.*/
065    private ZipFile m_zipFile;
066
067    /**
068     * Constructor.<p>
069     *
070     * @param parameters the import parameters to use
071     */
072    public CmsImportHelper(CmsImportParameters parameters) {
073
074        m_params = parameters;
075    }
076
077    /**
078     * Adds a new DTD system id prefix mapping for internal resolution
079     * of external URLs.<p>
080     *
081     * @param dtdSystemLocation the internal system location of the DTD file, e.g. <code>org/opencms/configuration/</code>
082     * @param dtdFilename the name of the DTD file, e.g. <code>opencms-configuration.dtd</code>
083     * @param dtdUrlPrefix the external system id prefix of the DTD file, e.g. <code>http://www.opencms.org/dtd/6.0/</code>
084     *
085     * @see org.opencms.configuration.I_CmsXmlConfiguration
086     */
087    public void cacheDtdSystemId(String dtdSystemLocation, String dtdFilename, String dtdUrlPrefix) {
088
089        if (dtdSystemLocation != null) {
090            try {
091                String file = CmsFileUtil.readFile(dtdSystemLocation + dtdFilename, CmsEncoder.ENCODING_UTF_8);
092                CmsXmlEntityResolver.cacheSystemId(
093                    dtdUrlPrefix + dtdFilename,
094                    file.getBytes(CmsEncoder.ENCODING_UTF_8));
095                if (LOG.isDebugEnabled()) {
096                    LOG.debug(
097                        org.opencms.configuration.Messages.get().getBundle().key(
098                            org.opencms.configuration.Messages.LOG_CACHE_DTD_SYSTEM_ID_1,
099                            new Object[] {dtdUrlPrefix + dtdFilename + " --> " + dtdSystemLocation + dtdFilename}));
100                }
101            } catch (IOException e) {
102                LOG.error(
103                    org.opencms.configuration.Messages.get().getBundle().key(
104                        org.opencms.configuration.Messages.LOG_CACHE_DTD_SYSTEM_ID_FAILURE_1,
105                        new Object[] {dtdSystemLocation + dtdFilename}),
106                    e);
107            }
108        }
109    }
110
111    /**
112     * Closes the zip file.<p>
113     */
114    public void closeFile() {
115
116        if (getZipFile() != null) {
117            try {
118                getZipFile().close();
119            } catch (IOException e) {
120                CmsMessageContainer message = Messages.get().container(
121                    Messages.ERR_IMPORTEXPORT_ERROR_CLOSING_ZIP_ARCHIVE_1,
122                    getZipFile().getName());
123                if (LOG.isDebugEnabled()) {
124                    LOG.debug(message.key(), e);
125                }
126            }
127        }
128    }
129
130    /**
131     * Returns a byte array containing the content of the file.<p>
132     *
133     * @param filename the name of the file to read, relative to the folder or zip file
134     *
135     * @return a byte array containing the content of the file
136     *
137     * @throws CmsImportExportException if something goes wrong
138     */
139    public byte[] getFileBytes(String filename) throws CmsImportExportException {
140
141        try {
142            // is this a zip-file?
143            if (getZipFile() != null) {
144
145                ZipEntry entry = getZipEntry(filename);
146                InputStream stream = getZipFile().getInputStream(entry);
147                int size = Long.valueOf(entry.getSize()).intValue();
148                return CmsFileUtil.readFully(stream, size);
149            } else {
150                // no - use directory
151                File file = getFile(filename);
152                return CmsFileUtil.readFile(file);
153            }
154        } catch (FileNotFoundException fnfe) {
155            CmsMessageContainer msg = Messages.get().container(Messages.ERR_IMPORTEXPORT_FILE_NOT_FOUND_1, filename);
156            if (LOG.isErrorEnabled()) {
157                LOG.error(msg.key(), fnfe);
158            }
159            throw new CmsImportExportException(msg, fnfe);
160        } catch (IOException ioe) {
161            CmsMessageContainer msg = Messages.get().container(
162                Messages.ERR_IMPORTEXPORT_ERROR_READING_FILE_1,
163                filename);
164            if (LOG.isErrorEnabled()) {
165                LOG.error(msg.key(), ioe);
166            }
167            throw new CmsImportExportException(msg, ioe);
168        }
169    }
170
171    public long getFileModification(String filename) throws CmsImportExportException {
172
173        long modificationTime = 0;
174
175        try {
176            // is this a zip-file?
177            if (getZipFile() != null) {
178                // yes
179                ZipEntry entry = getZipEntry(filename);
180                modificationTime = entry.getTime();
181            } else {
182                // no - use directory
183                File file = getFile(filename);
184                modificationTime = file.lastModified();
185            }
186            if (modificationTime < 0) {
187                return 0;
188            } else {
189                return modificationTime;
190            }
191        } catch (IOException ioe) {
192            CmsMessageContainer msg = Messages.get().container(
193                Messages.ERR_IMPORTEXPORT_ERROR_READING_FILE_1,
194                filename);
195            if (LOG.isErrorEnabled()) {
196                LOG.error(msg.key(), ioe);
197            }
198            throw new CmsImportExportException(msg, ioe);
199        }
200    }
201
202    /**
203     * Returns the name of the import file, without zip extension.<p>
204     *
205     * @return the name of the import file, without zip extension
206     */
207    public String getFileName() {
208
209        String fileName = m_params.getPath().replace('\\', '/');
210        String zipName = fileName.substring(fileName.lastIndexOf('/') + 1);
211        String result;
212
213        if (zipName.toLowerCase().endsWith(".zip")) {
214            result = zipName.substring(0, zipName.lastIndexOf('.'));
215            int pos = result.lastIndexOf('_');
216            if (pos > 0) {
217                result = result.substring(0, pos);
218            }
219        } else {
220            result = zipName;
221        }
222        return result;
223    }
224
225    /**
226     * Returns a stream for the content of the file.<p>
227     *
228     * @param fileName the name of the file to stream, relative to the folder or zip file
229     *
230     * @return an input stream for the content of the file, remember to close it after using
231     *
232     * @throws CmsImportExportException if something goes wrong
233     */
234    public InputStream getFileStream(String fileName) throws CmsImportExportException {
235
236        try {
237            InputStream stream = null;
238            // is this a zip-file?
239            if (getZipFile() != null) {
240                // yes
241                ZipEntry entry = getZipFile().getEntry(fileName);
242                // path to file might be relative, too
243                if ((entry == null) && fileName.startsWith("/")) {
244                    entry = getZipFile().getEntry(fileName.substring(1));
245                }
246                if (entry == null) {
247                    throw new ZipException(
248                        Messages.get().getBundle().key(Messages.LOG_IMPORTEXPORT_FILE_NOT_FOUND_IN_ZIP_1, fileName));
249                }
250
251                stream = getZipFile().getInputStream(entry);
252            } else {
253                // no - use directory
254                File file = new File(getFolder(), CmsImportExportManager.EXPORT_MANIFEST);
255                stream = new FileInputStream(file);
256            }
257            return stream;
258        } catch (Exception ioe) {
259            CmsMessageContainer msg = Messages.get().container(
260                Messages.ERR_IMPORTEXPORT_ERROR_READING_FILE_1,
261                fileName);
262            if (LOG.isErrorEnabled()) {
263                LOG.error(msg.key(), ioe);
264            }
265            throw new CmsImportExportException(msg, ioe);
266        }
267    }
268
269    /**
270     * Returns the RFS folder to import from.<p>
271     *
272     * @return the RFS folder to import from
273     */
274    public File getFolder() {
275
276        return m_folder;
277    }
278
279    /**
280     * Returns the class system location.<p>
281     *
282     * i.e: <code>org/opencms/importexport</code><p>
283     *
284     * @param clazz the class to get the location for
285     *
286     * @return the class system location
287     */
288    public String getLocation(Class<?> clazz) {
289
290        String filename = clazz.getName().replace('.', '/');
291        int pos = filename.lastIndexOf('/') + 1;
292        return (pos > 0 ? filename.substring(0, pos) : "");
293    }
294
295    /**
296     * Returns the RFS zip file to import from.<p>
297     *
298     * @return the RFS zip file to import from
299     */
300    public ZipFile getZipFile() {
301
302        return m_zipFile;
303    }
304
305    /**
306     * Opens the import file.<p>
307     *
308     * @throws IOException if the file could not be opened
309     */
310    public void openFile() throws IOException {
311
312        // get the import resource
313        m_folder = new File(OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebInf(m_params.getPath()));
314
315        // if it is a file it must be a zip-file
316        if (m_folder.isFile()) {
317            m_zipFile = new ZipFile(m_params.getPath());
318            m_folder = null;
319        }
320    }
321
322    /** Returns the file for the provided filename.
323     * @param filename name of the file
324     * @return the file.
325     */
326    protected File getFile(String filename) {
327
328        return new File(getFolder(), filename);
329    }
330
331    /** Returns the zip entry for a file in the archive.
332     * @param filename the file name
333     * @return the zip entry for the file with the provided name
334     * @throws ZipException thrown if the file is not in the zip archive
335     */
336    protected ZipEntry getZipEntry(String filename) throws ZipException {
337
338        // yes
339        ZipEntry entry = getZipFile().getEntry(filename);
340        // path to file might be relative, too
341        if ((entry == null) && filename.startsWith("/")) {
342            entry = m_zipFile.getEntry(filename.substring(1));
343        }
344        if (entry == null) {
345            throw new ZipException(
346                Messages.get().getBundle().key(Messages.LOG_IMPORTEXPORT_FILE_NOT_FOUND_IN_ZIP_1, filename));
347        }
348        return entry;
349    }
350}