/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.core.commons.services.webdav.manager; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.Collections; import java.util.List; import org.apache.poi.util.IOUtils; import org.olat.core.commons.modules.bc.meta.MetaInfo; import org.olat.core.commons.modules.bc.meta.tagged.MetaTagged; import org.olat.core.commons.services.notifications.NotificationsManager; import org.olat.core.commons.services.notifications.SubscriptionContext; import org.olat.core.commons.services.webdav.servlets.WebResource; import org.olat.core.commons.services.webdav.servlets.WebResourceRoot; import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.vfs.Quota; import org.olat.core.util.vfs.QuotaExceededException; import org.olat.core.util.vfs.VFSConstants; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSManager; import org.olat.core.util.vfs.VFSStatus; import org.olat.core.util.vfs.callbacks.VFSSecurityCallback; import org.olat.core.util.vfs.version.Versionable; import org.olat.core.util.vfs.version.VersionsManager; /** * * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class VFSResourceRoot implements WebResourceRoot { private static final OLog log = Tracing.createLoggerFor(VFSResourceRoot.class); private static final int BUFFER_SIZE = 2048; private final Identity identity; private final VFSContainer base; public VFSResourceRoot(Identity identity, VFSContainer root) { this.identity = identity; this.base = root; } public Identity getIdentity() { return identity; } public VFSContainer getRoot() { return base; } @Override public boolean canWrite(String name) { // resolve item if it already exists VFSItem item = resolveFile(name); if (item == null) { // try to resolve parent in case the item does not yet exist int lastSlash = name.lastIndexOf("/"); if (lastSlash > 0) { String containerName = name.substring(0, lastSlash); item = resolveFile(containerName); } } if (item == null) { return false; } VFSStatus status; if (item instanceof VFSContainer) { status = item.canWrite(); } else { // read/write is not defined on item level, only on directory level status = item.getParentContainer().canWrite(); } return VFSConstants.YES.equals(status); } @Override public boolean canRename(String name) { VFSItem item = resolveFile(name); if (item != null && VFSConstants.YES.equals(item.canRename())) { return true; } else { return false; } } @Override public boolean canDelete(String path) { VFSItem item = resolveFile(path); if (item != null && VFSConstants.YES.equals(item.canDelete())) { return true; } else { return false; } } @Override public WebResource getResource(String path) { VFSItem file = resolveFile(path); if(file == null) { return new EmptyWebResource(path); } return new VFSResource(file, path); } @Override public Collection<VFSItem> list(String path) { VFSItem file = resolveFile(path); if(file instanceof VFSContainer) { VFSContainer container = (VFSContainer)file; List<VFSItem> children = container.getItems(); return children; } else { return Collections.emptyList(); } } @Override public boolean mkdir(String path) { //remove trailing / if(path.endsWith("/")) { path = path.substring(0, path.length() - 1); } int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1) return false; String parentPath = path.substring(0, lastSlash); VFSItem parentItem = resolveFile(parentPath); if (parentItem instanceof VFSLeaf) { return false; } else if (parentItem instanceof VFSContainer) { String name = path.substring(lastSlash + 1); VFSContainer folder = (VFSContainer)parentItem; if(folder.canWrite() == VFSConstants.YES) { VFSContainer dir = folder.createChildContainer(name); return dir != null && dir.exists(); } } return false; } @Override public boolean write(String path, InputStream is, boolean overwrite, WebResource movedFrom) throws QuotaExceededException { VFSLeaf childLeaf; VFSItem file = resolveFile(path); if (file instanceof VFSLeaf) { if(overwrite) { //overwrite the file childLeaf = (VFSLeaf)file; //versioning if(childLeaf instanceof Versionable && ((Versionable)childLeaf).getVersions().isVersioned()) { if(childLeaf.getSize() == 0) { VersionsManager.getInstance().createVersionsFor(childLeaf, true); } else { VersionsManager.getInstance().addToRevisions((Versionable)childLeaf, identity, ""); } } } else { return false; } } else if (file instanceof VFSContainer) { return false; } else { //create a new file int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1) return false; String parentPath = path.substring(0, lastSlash); VFSItem parentItem = resolveFile(parentPath); if(parentItem instanceof VFSContainer) { VFSContainer folder = (VFSContainer)parentItem; String name = path.substring(lastSlash + 1); childLeaf = folder.createChildLeaf(name); } else { return false; } } if(childLeaf == null) { return false; } try { copyVFS(childLeaf, is); } catch (QuotaExceededException e) { throw e; } catch (Exception e) { log.error("", e); return false; } VFSContainer inheritingCont = VFSManager.findInheritingSecurityCallbackContainer(childLeaf.getParentContainer()); if(inheritingCont != null) { VFSSecurityCallback callback = inheritingCont.getLocalSecurityCallback(); if(callback != null && callback.getSubscriptionContext() != null) { SubscriptionContext subContext = callback.getSubscriptionContext(); NotificationsManager.getInstance().markPublisherNews(subContext, null, true); } } if(childLeaf instanceof MetaTagged && identity != null) { MetaInfo infos = ((MetaTagged)childLeaf).getMetaInfo(); if(infos != null && !infos.hasAuthorIdentity()) { infos.setAuthor(identity); infos.clearThumbnails(); //infos.write(); the clearThumbnails call write() } } if(movedFrom instanceof VFSResource) { VFSResource vfsResource = (VFSResource)movedFrom; if(vfsResource.getItem() instanceof Versionable && ((Versionable)vfsResource.getItem()).getVersions().isVersioned()) { VFSLeaf currentVersion = (VFSLeaf)vfsResource.getItem(); VersionsManager.getInstance().move(currentVersion, childLeaf, identity); } } return true; } private void copyVFS(VFSLeaf file, InputStream is) throws IOException { // Try to get Quota long quotaLeft = -1; boolean withQuotaCheck = false; VFSContainer parentContainer = file.getParentContainer(); if (parentContainer != null) { quotaLeft = VFSManager.getQuotaLeftKB(parentContainer); if (quotaLeft != Quota.UNLIMITED) { quotaLeft = quotaLeft * 1024; // convert from kB withQuotaCheck = true; } else { withQuotaCheck = false; } } // Open os OutputStream os = null; byte buffer[] = new byte[BUFFER_SIZE]; int len = -1; boolean quotaExceeded = false; try { os = file.getOutputStream(false); while (true) { len = is.read(buffer); if (len == -1) break; if (withQuotaCheck) { // re-calculate quota and check quotaLeft = quotaLeft - len; if (quotaLeft < 0) { log.info("Quota exceeded: " + file); quotaExceeded = true; break; } } os.write(buffer, 0, len); } if(quotaExceeded) { IOUtils.closeQuietly(os); file.delete(); throw new QuotaExceededException(""); } } catch (IOException e) { IOUtils.closeQuietly(os); // close first, in order to be able to delete any reamins of the file file.delete(); throw e; } finally { IOUtils.closeQuietly(os); IOUtils.closeQuietly(is); } } @Override public boolean delete(WebResource resource) { boolean deleted = false; if(resource instanceof VFSResource) { VFSResource vfsResource = (VFSResource)resource; VFSItem item = vfsResource.getItem(); if (item != null && VFSConstants.YES.equals(item.canDelete())) { VFSStatus status = item.delete(); deleted = (status == VFSConstants.YES || status == VFSConstants.SUCCESS); } } return deleted; } /** * Resolve a file relative to this base. * Make sure, paths are relative */ private VFSItem resolveFile(String name) { if (name == null) name = ""; if (name.length() > 0 && name.charAt(0) == '/') name = name.substring(1); return base.resolve(name); } }