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.db; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsProperty; 033import org.opencms.file.CmsPropertyDefinition; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResourceFilter; 036import org.opencms.file.CmsVfsException; 037import org.opencms.file.types.CmsResourceTypeFolder; 038import org.opencms.file.types.CmsResourceTypePlain; 039import org.opencms.main.CmsException; 040import org.opencms.main.CmsLog; 041import org.opencms.main.OpenCms; 042import org.opencms.security.CmsSecurityException; 043import org.opencms.util.CmsFileUtil; 044 045import java.io.File; 046import java.io.IOException; 047import java.util.ArrayList; 048import java.util.List; 049import java.util.StringTokenizer; 050 051import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; 052import org.apache.commons.compress.archivers.zip.ZipFile; 053import org.apache.commons.logging.Log; 054 055/** 056 * Allows to import resources from the filesystem or a ZIP file into the OpenCms VFS.<p> 057 * 058 * @since 6.0.0 059 */ 060public class CmsImportFolder { 061 062 /** Logger instance for this class. */ 063 private static final Log LOG = CmsLog.getLog(CmsImportFolder.class); 064 065 /** The OpenCms context object that provides the permissions. */ 066 private CmsObject m_cms; 067 068 /** The names of resources that have been created or replaced during the import. */ 069 private List<CmsResource> m_importedResources = new ArrayList<CmsResource>(); 070 071 /** The name of the import folder to load resources from. */ 072 private String m_importFolderName; 073 074 /** The import path in the OpenCms VFS. */ 075 private String m_importPath; 076 077 /** The resource (folder or ZIP file) to import from in the real file system. */ 078 private File m_importResource; 079 080 /** Will be true if the import resource is a valid ZIP file. */ 081 private boolean m_validZipFile; 082 083 /** The import resource ZIP to load resources from. */ 084 private ZipFile m_zipFile; 085 086 /** 087 * Default Constructor.<p> 088 */ 089 public CmsImportFolder() { 090 091 // noop 092 } 093 094 /** 095 * Constructor for a new CmsImportFolder that will read from a ZIP file.<p> 096 * 097 * @param content the zip file to import 098 * @param importPath the path to the OpenCms VFS to import to 099 * @param cms a OpenCms context to provide the permissions 100 * @param noSubFolder if <code>true</code> no sub folders will be created, if <code>false</code> the content of the 101 * zip file is created 1:1 inclusive sub folders 102 * 103 * @throws CmsException if something goes wrong 104 */ 105 public CmsImportFolder(byte[] content, String importPath, CmsObject cms, boolean noSubFolder) 106 throws CmsException { 107 108 importZip(content, importPath, cms, noSubFolder); 109 } 110 111 /** 112 * Constructor for a new CmsImportFolder that will read from the real file system.<p> 113 * 114 * @param importFolderName the folder to import 115 * @param importPath the path to the OpenCms VFS to import to 116 * @param cms a OpenCms context to provide the permissions 117 * @throws CmsException if something goes wrong 118 */ 119 public CmsImportFolder(String importFolderName, String importPath, CmsObject cms) 120 throws CmsException { 121 122 importFolder(importFolderName, importPath, cms); 123 } 124 125 /** 126 * Returns the list of imported resources.<p> 127 * 128 * @return the list of imported resources 129 */ 130 public List<CmsResource> getImportedResources() { 131 132 return m_importedResources; 133 } 134 135 /** 136 * Import that will read from the real file system.<p> 137 * 138 * @param importFolderName the folder to import 139 * @param importPath the path to the OpenCms VFS to import to 140 * @param cms a OpenCms context to provide the permissions 141 * @throws CmsException if something goes wrong 142 */ 143 public void importFolder(String importFolderName, String importPath, CmsObject cms) throws CmsException { 144 145 try { 146 m_importedResources = new ArrayList<CmsResource>(); 147 m_importFolderName = importFolderName; 148 m_importPath = importPath; 149 m_cms = cms; 150 // open the import resource 151 getImportResource(); 152 // first lock the destination path 153 m_cms.lockResource(m_importPath); 154 // import the resources 155 if (m_zipFile == null) { 156 importResources(m_importResource, m_importPath); 157 } else { 158 importZipResource(m_zipFile, m_importPath, false); 159 } 160 // all is done, unlock the resources 161 m_cms.unlockResource(m_importPath); 162 } catch (Exception e) { 163 throw new CmsVfsException( 164 Messages.get().container(Messages.ERR_IMPORT_FOLDER_2, importFolderName, importPath), 165 e); 166 } finally { 167 if (m_zipFile != null) { 168 try { 169 m_zipFile.close(); 170 } catch (Exception e) { 171 LOG.info(e.getLocalizedMessage(), e); 172 } 173 } 174 } 175 176 } 177 178 /** 179 * Import that will read from a ZIP file.<p> 180 * 181 * @param content the zip file to import 182 * @param importPath the path to the OpenCms VFS to import to 183 * @param cms a OpenCms context to provide the permissions 184 * @param noSubFolder if <code>true</code> no sub folders will be created, if <code>false</code> the content of the 185 * zip file is created 1:1 inclusive sub folders 186 * 187 * @throws CmsException if something goes wrong 188 */ 189 public void importZip(byte[] content, String importPath, CmsObject cms, boolean noSubFolder) throws CmsException { 190 191 m_importPath = importPath; 192 m_cms = cms; 193 try { 194 m_zipFile = ZipFile.builder().setByteArray(content).setBufferSize(65536).get(); 195 m_cms.readFolder(importPath, CmsResourceFilter.IGNORE_EXPIRATION); 196 // import the resources 197 importZipResource(m_zipFile, m_importPath, noSubFolder); 198 } catch (Exception e) { 199 throw new CmsVfsException(Messages.get().container(Messages.ERR_IMPORT_FOLDER_1, importPath), e); 200 } finally { 201 if (m_zipFile != null) { 202 try { 203 m_zipFile.close(); 204 } catch (Exception e) { 205 LOG.info(e.getLocalizedMessage()); 206 } 207 } 208 } 209 210 } 211 212 /** 213 * Returns true if a valid ZIP file was imported.<p> 214 * 215 * @return true if a valid ZIP file was imported 216 */ 217 public boolean isValidZipFile() { 218 219 return m_validZipFile; 220 } 221 222 /** 223 * Stores the import resource in an Object member variable.<p> 224 * @throws CmsVfsException if the file to import is no valid zipfile 225 */ 226 private void getImportResource() throws CmsVfsException { 227 228 // get the import resource 229 m_importResource = new File(m_importFolderName); 230 // check if this is a folder or a ZIP file 231 if (m_importResource.isFile()) { 232 try { 233 m_zipFile = ZipFile.builder().setFile(m_importResource).setBufferSize(65536).get(); 234 } catch (IOException e) { 235 // if file but no ZIP file throw an exception 236 throw new CmsVfsException( 237 Messages.get().container(Messages.ERR_NO_ZIPFILE_1, m_importResource.getName()), 238 e); 239 } 240 } 241 } 242 243 /** 244 * Imports the resources from the folder in the real file system to the OpenCms VFS.<p> 245 * 246 * @param folder the folder to import from 247 * @param importPath the OpenCms VFS import path to import to 248 * @throws Exception if something goes wrong during file IO 249 */ 250 private void importResources(File folder, String importPath) throws Exception { 251 252 String[] diskFiles = folder.list(); 253 File currentFile; 254 255 for (int i = 0; i < diskFiles.length; i++) { 256 currentFile = new File(folder, diskFiles[i]); 257 258 if (currentFile.isDirectory()) { 259 // create directory in cms 260 m_importedResources.add( 261 m_cms.createResource(importPath + currentFile.getName(), CmsResourceTypeFolder.RESOURCE_TYPE_ID)); 262 importResources(currentFile, importPath + currentFile.getName() + "/"); 263 } else { 264 // import file into cms 265 int type = OpenCms.getResourceManager().getDefaultTypeForName(currentFile.getName()).getTypeId(); 266 byte[] content = CmsFileUtil.readFile(currentFile); 267 // create the file 268 try { 269 m_importedResources.add( 270 m_cms.createResource(importPath + currentFile.getName(), type, content, null)); 271 } catch (CmsSecurityException e) { 272 // in case of not enough permissions, try to create a plain text file 273 int plainId = OpenCms.getResourceManager().getResourceType( 274 CmsResourceTypePlain.getStaticTypeName()).getTypeId(); 275 m_importedResources.add( 276 m_cms.createResource(importPath + currentFile.getName(), plainId, content, null)); 277 } 278 content = null; 279 } 280 } 281 } 282 283 /** 284 * Imports the resources from a ZIP file in the real file system to the OpenCms VFS.<p> 285 * 286 * @param zipStreamIn the input Stream 287 * @param importPath the path in the vfs 288 * @param noSubFolder if <code>true</code> no sub folders will be created, if <code>false</code> the content of the 289 * zip file is created 1:1 inclusive sub folders 290 * 291 * @throws Exception if something goes wrong during file IO 292 */ 293 private void importZipResource(ZipFile zipFile, String importPath, boolean noSubFolder) throws Exception { 294 295 // HACK: this method looks very crude, it should be re-written sometime... 296 297 boolean isFolder = false; 298 int j, r, stop, size; 299 int entries = 0; 300 byte[] buffer = null; 301 boolean resourceExists; 302 for (ZipArchiveEntry entry : (Iterable<ZipArchiveEntry>)() -> zipFile.getEntries().asIterator()) { 303 // handle the single entries ... 304 j = 0; 305 stop = 0; 306 entries++; // count number of entries in zip 307 String actImportPath = importPath; 308 String title = CmsResource.getName(entry.getName()); 309 String filename = m_cms.getRequestContext().getFileTranslator().translateResource(entry.getName()); 310 // separate path in directories an file name ... 311 StringTokenizer st = new StringTokenizer(filename, "/\\"); 312 int count = st.countTokens(); 313 String[] path = new String[count]; 314 315 if (filename.endsWith("\\") || filename.endsWith("/")) { 316 isFolder = true; // last entry is a folder 317 } else { 318 isFolder = false; // last entry is a file 319 } 320 while (st.hasMoreTokens()) { 321 // store the files and folder names in array ... 322 path[j] = st.nextToken(); 323 j++; 324 } 325 stop = isFolder ? path.length : (path.length - 1); 326 327 if (noSubFolder) { 328 stop = 0; 329 } 330 // now write the folders ... 331 for (r = 0; r < stop; r++) { 332 try { 333 CmsResource createdFolder = m_cms.createResource( 334 actImportPath + path[r], 335 CmsResourceTypeFolder.RESOURCE_TYPE_ID); 336 m_importedResources.add(createdFolder); 337 } catch (CmsException e) { 338 // of course some folders did already exist! 339 } 340 actImportPath += path[r]; 341 actImportPath += "/"; 342 } 343 if (!isFolder) { 344 // import file into cms 345 int type = OpenCms.getResourceManager().getDefaultTypeForName(path[path.length - 1]).getTypeId(); 346 buffer = CmsFileUtil.readFully(zipFile.getInputStream(entry), true); 347 size = Long.valueOf(entry.getSize()).intValue(); 348 filename = actImportPath + path[path.length - 1]; 349 try { 350 m_cms.lockResource(filename); 351 m_cms.readResource(filename); 352 resourceExists = true; 353 } catch (CmsException e) { 354 resourceExists = false; 355 } 356 357 int plainId = OpenCms.getResourceManager().getResourceType( 358 CmsResourceTypePlain.getStaticTypeName()).getTypeId(); 359 if (resourceExists) { 360 CmsResource res = m_cms.readResource(filename, CmsResourceFilter.ALL); 361 CmsFile file = m_cms.readFile(res); 362 byte[] contents = file.getContents(); 363 try { 364 m_cms.replaceResource(filename, res.getTypeId(), buffer, new ArrayList<CmsProperty>(0)); 365 m_importedResources.add(res); 366 } catch (CmsSecurityException e) { 367 // in case of not enough permissions, try to create a plain text file 368 m_cms.replaceResource(filename, plainId, buffer, new ArrayList<CmsProperty>(0)); 369 m_importedResources.add(res); 370 } catch (CmsDbSqlException sqlExc) { 371 // SQL error, probably the file is too large for the database settings, restore content 372 file.setContents(contents); 373 m_cms.writeFile(file); 374 throw sqlExc; 375 } 376 } else { 377 String newResName = actImportPath + path[path.length - 1]; 378 if (title.lastIndexOf('.') != -1) { 379 title = title.substring(0, title.lastIndexOf('.')); 380 } 381 List<CmsProperty> properties = new ArrayList<CmsProperty>(1); 382 CmsProperty titleProp = new CmsProperty(); 383 titleProp.setName(CmsPropertyDefinition.PROPERTY_TITLE); 384 if (OpenCms.getWorkplaceManager().isDefaultPropertiesOnStructure()) { 385 titleProp.setStructureValue(title); 386 } else { 387 titleProp.setResourceValue(title); 388 } 389 properties.add(titleProp); 390 try { 391 m_importedResources.add(m_cms.createResource(newResName, type, buffer, properties)); 392 } catch (CmsSecurityException e) { 393 // in case of not enough permissions, try to create a plain text file 394 m_importedResources.add(m_cms.createResource(newResName, plainId, buffer, properties)); 395 } catch (CmsDbSqlException sqlExc) { 396 // SQL error, probably the file is too large for the database settings, delete file 397 m_cms.lockResource(newResName); 398 m_cms.deleteResource(newResName, CmsResource.DELETE_PRESERVE_SIBLINGS); 399 throw sqlExc; 400 } 401 } 402 } 403 } 404 if (entries > 0) { 405 // at least one entry, got a valid zip file ... 406 m_validZipFile = true; 407 } 408 } 409}