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