/* * LinShare is an open source filesharing software, part of the LinPKI software * suite, developed by Linagora. * * Copyright (C) 2015 LINAGORA * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version, provided you comply with the Additional Terms applicable for * LinShare software by Linagora pursuant to Section 7 of the GNU Affero General * Public License, subsections (b), (c), and (e), pursuant to which you must * notably (i) retain the display of the “LinShare™” trademark/logo at the top * of the interface window, the display of the “You are using the Open Source * and free version of LinShare™, powered by Linagora © 2009–2015. Contribute to * Linshare R&D by subscribing to an Enterprise offer!” infobox and in the * e-mails sent with the Program, (ii) retain all hypertext links between * LinShare and linshare.org, between linagora.com and Linagora, and (iii) * refrain from infringing Linagora intellectual property rights over its * trademarks and commercial brands. Other Additional Terms apply, see * <http://www.linagora.com/licenses/> for more details. * * This program 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 Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License and * its applicable Additional Terms for LinShare along with this program. If not, * see <http://www.gnu.org/licenses/> for the GNU Affero General Public License * version 3 and <http://www.linagora.com/licenses/> for the Additional Terms * applicable to LinShare software. */ package org.linagora.linshare.cmis.facade.impl; import java.math.BigInteger; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.chemistry.opencmis.commons.data.ContentStream; import org.apache.chemistry.opencmis.commons.data.ExtensionsData; import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData; import org.apache.chemistry.opencmis.commons.data.ObjectData; import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer; import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData; import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList; import org.apache.chemistry.opencmis.commons.data.ObjectParentData; import org.apache.chemistry.opencmis.commons.data.Properties; import org.apache.chemistry.opencmis.commons.data.RepositoryInfo; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList; import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges; import org.apache.chemistry.opencmis.commons.enums.CmisVersion; import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; import org.apache.chemistry.opencmis.commons.enums.UnfileObject; import org.apache.chemistry.opencmis.commons.enums.VersioningState; import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException; import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; import org.apache.chemistry.opencmis.commons.exceptions.CmisPermissionDeniedException; import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectDataImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderListImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectParentDataImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryCapabilitiesImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionListImpl; import org.apache.chemistry.opencmis.commons.impl.server.AbstractCmisService; import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl; import org.apache.chemistry.opencmis.commons.server.CmisService; import org.apache.chemistry.opencmis.commons.server.ObjectInfo; import org.apache.chemistry.opencmis.commons.spi.Holder; import org.apache.chemistry.opencmis.server.support.TypeDefinitionFactory; import org.linagora.linshare.cmis.constants.CmisConstants; import org.linagora.linshare.cmis.constants.CmisDirectory; import org.linagora.linshare.cmis.constants.CmisStrings; import org.linagora.linshare.cmis.facade.MainCmisFacade; import org.linagora.linshare.cmis.utils.CmisHelpers; import org.linagora.linshare.core.domain.constants.SupportedLanguage; import org.linagora.linshare.core.domain.entities.Account; import org.linagora.linshare.core.domain.entities.Functionality; import org.linagora.linshare.core.service.AccountService; import org.linagora.linshare.core.service.FunctionalityReadOnlyService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; public class MainCmisFacadeImpl extends AbstractCmisService implements MainCmisFacade { private final CmisVersion cmisVersion = CmisVersion.CMIS_1_0; private final AccountService accountService; private TypeDefinitionFactory typeDefinitionFactory = TypeDefinitionFactory .newInstance(); private TypeDefinition folderTypeDefinition = typeDefinitionFactory .createBaseFolderTypeDefinition(cmisVersion); private final Logger log = LoggerFactory .getLogger(MainCmisFacadeImpl.class); private TypeDefinition documentTypeDefinition = typeDefinitionFactory .createBaseDocumentTypeDefinition(cmisVersion); private final CmisService documentEntryCmisService; private final CmisService threadEntryCmisService; private final CmisHelpers helpers; private final CmisStrings cmisStrings; private final FunctionalityReadOnlyService functionalityReadOnlyService; public MainCmisFacadeImpl(AccountService accountService, CmisService documentEntryCmisService, CmisService threadEntryCmisService, CmisStrings cmisStrings, FunctionalityReadOnlyService functionalityReadOnlyService, CmisHelpers cmisHelpers) { this.accountService = accountService; this.documentEntryCmisService = documentEntryCmisService; this.threadEntryCmisService = threadEntryCmisService; this.cmisStrings = cmisStrings; this.functionalityReadOnlyService = functionalityReadOnlyService; this.helpers = cmisHelpers; } @Override public ContentStream getContentStream(String repositoryId, String objectId, String streamId, BigInteger offset, BigInteger length, ExtensionsData extension) { if (objectId.startsWith(CmisConstants.tagDocumentEntry)) return documentEntryCmisService.getContentStream(repositoryId, objectId, streamId, offset, length, extension); if (objectId.startsWith(CmisConstants.tagThreadEntry)) return threadEntryCmisService.getContentStream(repositoryId, objectId, streamId, offset, length, extension); throw new CmisObjectNotFoundException(); } private Account authenticate() { Authentication auth = SecurityContextHolder.getContext() .getAuthentication(); if (auth == null) { log.debug("Not authenticated access"); throw new CmisPermissionDeniedException(); } log.debug("Authenticated Principal :" + auth.getName()); Account currentReq = (Account) RequestContextHolder .getRequestAttributes().getAttribute( "org.linagora.linshare.cmis.currentUser", RequestAttributes.SCOPE_REQUEST); if (currentReq != null) return currentReq; Account account = accountService.findByLsUuid(auth.getName()); Functionality cmisFunc = functionalityReadOnlyService .getCmisFunctionality(account.getDomain()); if (!cmisFunc.getActivationPolicy().getStatus()) throw new CmisPermissionDeniedException( "Not Authorized to use CMIS !"); return account; } @Override public List<RepositoryInfo> getRepositoryInfos(ExtensionsData extension) { RepositoryInfoImpl r = new RepositoryInfoImpl(); DateFormat dateFormat = new SimpleDateFormat( "MM-dd-yyyy_HH'h'mm'm'ss's'"); Account account = authenticate(); r.setId(account.getLsUuid()); r.setName(account.getFullName() + "_" + dateFormat.format(account.getCreationDate())); r.setRootFolder(CmisConstants.tagRoot); r.setCmisVersion(cmisVersion); RepositoryCapabilitiesImpl capabilities = new RepositoryCapabilitiesImpl(); capabilities.setCapabilityChanges(CapabilityChanges.NONE); capabilities.setSupportsGetDescendants(false); capabilities.setSupportsGetFolderTree(false); r.setCapabilities(capabilities); List<RepositoryInfo> res = new LinkedList<RepositoryInfo>(); res.add(r); return res; } @Override public TypeDefinitionList getTypeChildren(String repositoryId, String typeId, Boolean includePropertyDefinitions, BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) { TypeDefinitionList res = new TypeDefinitionListImpl( new ArrayList<TypeDefinition>()); return res; } @Override public List<ObjectInFolderContainer> getDescendants(String repositoryId, String folderId, BigInteger depth, String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, Boolean includePathSegment, ExtensionsData extension) { return super.getDescendants(repositoryId, folderId, depth, filter, includeAllowableActions, includeRelationships, renditionFilter, includePathSegment, extension); } @Override public TypeDefinition getTypeDefinition(String repositoryId, String typeId, ExtensionsData extension) { authenticate(); TypeDefinition res = null; if (typeId.equals(BaseTypeId.CMIS_FOLDER.value())) { res = folderTypeDefinition; } if (typeId.equals(BaseTypeId.CMIS_DOCUMENT.value())) { res = documentTypeDefinition; } return res; } @Override public ObjectInFolderList getChildren(String repositoryId, String folderId, String filter, String orderBy, Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) { Account account = authenticate(); ObjectInFolderListImpl res = new ObjectInFolderListImpl(); if (folderId.equals(CmisConstants.tagRoot)) { List<ObjectInFolderData> objDlist = new LinkedList<ObjectInFolderData>(); ObjectInFolderDataImpl objectInFolderDataImpl = null; boolean threadFunc = functionalityReadOnlyService .getCmisThreadsFunctionality(account.getDomain()) .getActivationPolicy().getStatus(); boolean docFunc = functionalityReadOnlyService .getCmisDocumentsFunctionality(account.getDomain()) .getActivationPolicy().getStatus(); if (threadFunc) { objectInFolderDataImpl = new ObjectInFolderDataImpl(); objectInFolderDataImpl.setObject(getObject(repositoryId, CmisConstants.tagThread, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false, extension)); objDlist.add(objectInFolderDataImpl); } if (docFunc) { objectInFolderDataImpl = new ObjectInFolderDataImpl(); objectInFolderDataImpl.setObject(getObject(repositoryId, CmisConstants.tagDocument, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false, extension)); objDlist.add(objectInFolderDataImpl); } res.setObjects(objDlist); } else if (folderId.equals(CmisConstants.tagDocument)) { res = (ObjectInFolderListImpl) documentEntryCmisService .getChildren(repositoryId, folderId, filter, orderBy, includeAllowableActions, includeRelationships, renditionFilter, includePathSegment, maxItems, skipCount, extension); } else if (folderId.startsWith(CmisConstants.tagThreadChildren) || folderId.equals(CmisConstants.tagThread)) { res = (ObjectInFolderListImpl) threadEntryCmisService.getChildren( repositoryId, folderId, filter, orderBy, includeAllowableActions, includeRelationships, renditionFilter, includePathSegment, maxItems, skipCount, extension); } else throw new CmisNotSupportedException(); return res; } @Override public List<ObjectParentData> getObjectParents(String repositoryId, String objectId, String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, Boolean includeRelativePathSegment, ExtensionsData extension) { authenticate(); String parentId = null; List<ObjectParentData> list = new LinkedList<ObjectParentData>(); if (objectId.equals(CmisConstants.tagRoot) || objectId.equals(CmisConstants.tagDocument) || objectId.equals(CmisConstants.tagThread)) { parentId = CmisConstants.tagRoot; } if (objectId.startsWith(CmisConstants.tagDocumentEntry)) { parentId = CmisConstants.tagDocument; } if (objectId.startsWith(CmisConstants.tagThreadChildren)) { parentId = CmisConstants.tagThread; } if (objectId.startsWith(CmisConstants.tagThreadEntry)) { list = threadEntryCmisService.getObjectParents(repositoryId, objectId, filter, includeAllowableActions, includeRelationships, renditionFilter, includeRelativePathSegment, extension); } else { ObjectParentDataImpl opd = new ObjectParentDataImpl(getObject( repositoryId, parentId, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false, extension)); opd.setRelativePathSegment(getObjectInfo(repositoryId, objectId) .getObject().getProperties().getPropertyList().get(4) .getFirstValue().toString()); list.add(opd); } return list; } @Override public ObjectData getObject(String repositoryId, String objectId, String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds, Boolean includeAcl, ExtensionsData extension) { authenticate(); ObjectDataImpl res = new ObjectDataImpl(); Map<String, ObjectData> memoizer = helpers.getMemoizer(); if (memoizer.get(objectId) != null) { res = (ObjectDataImpl) memoizer.get(objectId); } if (objectId.equals(CmisConstants.tagRoot)) { res.setProperties(helpers.setEntryProperty(objectId, "/", "")); res.setAllowableActions(helpers.setEntryAllowableActions(null, objectId)); memoizer.put(objectId, res); } else if (objectId.equals(CmisConstants.tagDocument) || objectId.startsWith(CmisConstants.tagDocumentEntry)) { res = (ObjectDataImpl) documentEntryCmisService.getObject( repositoryId, objectId, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension); memoizer.put(objectId, res); } else if (objectId.equals(CmisConstants.tagThread) || objectId.startsWith(CmisConstants.tagThreadChildren) || objectId.startsWith(CmisConstants.tagThreadEntry)) { res = (ObjectDataImpl) threadEntryCmisService.getObject( repositoryId, objectId, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension); memoizer.put(objectId, res); } else throw new CmisObjectNotFoundException(); return res; } @Override public ObjectData getObjectByPath(String repositoryId, String path, String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds, Boolean includeAcl, ExtensionsData extension) { Account actor = authenticate(); ObjectData res = null; if (!path.startsWith("/")) { throw new CmisNotSupportedException(); } String[] splitPath = path.substring(1).split("/"); if (splitPath.length == 1) { if (splitPath[0].equals("")) { res = getObject(repositoryId, CmisConstants.tagRoot, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension); } else if (splitPath[0].equals(cmisStrings.getName(CmisDirectory.MY_FILES, SupportedLanguage.fromTapestryLocale(actor.getCmisLocale())))) { res = getObject(repositoryId, CmisConstants.tagDocument, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension); } else throw new CmisObjectNotFoundException(); } else if (splitPath.length == 2) { if (splitPath[0].equals(cmisStrings.getName(CmisDirectory.MY_FILES, SupportedLanguage.fromTapestryLocale(actor.getCmisLocale())))) { res = documentEntryCmisService.getObjectByPath(repositoryId, path, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension); } else if (splitPath[0].equals(cmisStrings .getName(CmisDirectory.MY_THREADS, SupportedLanguage.fromTapestryLocale(actor.getCmisLocale())))) { res = threadEntryCmisService.getObjectByPath(repositoryId, path, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension); } else throw new CmisObjectNotFoundException(); } else throw new CmisObjectNotFoundException(); return res; } public ObjectInfo getObjectInfo(String repositoryId, String objectId) { ObjectInfoImpl res; if (objectId.equals(CmisConstants.tagRoot)) { res = new ObjectInfoImpl(objectId, BaseTypeId.CMIS_FOLDER); res.setObject(getObject(repositoryId, objectId, null, null, null, null, null, null, null)); return res; } else if (objectId.equals(CmisConstants.tagDocument) || objectId.startsWith(CmisConstants.tagDocumentEntry)) { res = (ObjectInfoImpl) documentEntryCmisService.getObjectInfo(repositoryId, objectId); } else if (objectId.equals(CmisConstants.tagThread) || objectId.startsWith(CmisConstants.tagThreadChildren) || objectId.startsWith(CmisConstants.tagThreadEntry)) { return threadEntryCmisService.getObjectInfo(repositoryId, objectId); } else throw new CmisObjectNotFoundException(); return res; } public RepositoryInfo getRepositoryInfo(String repositoryId, ExtensionsData extension) { RepositoryInfo repo = null; RepositoryInfo defau = null; List<RepositoryInfo> list = getRepositoryInfos(extension); for (RepositoryInfo r : list) { defau = r; if (r.getId().equals(repositoryId)) repo = r; } if (repo == null) { if (defau == null) { throw new CmisObjectNotFoundException(); } return defau; } return repo; } @Override public void deleteObjectOrCancelCheckOut(String repositoryId, String objectId, Boolean allVersions, ExtensionsData extension) { authenticate(); if (objectId.startsWith(CmisConstants.tagDocumentEntry)) { documentEntryCmisService.deleteObjectOrCancelCheckOut(repositoryId, objectId, allVersions, extension); } if (objectId.startsWith(CmisConstants.tagThreadEntry)) { threadEntryCmisService.deleteObjectOrCancelCheckOut(repositoryId, objectId, allVersions, extension); } } @Override public String create(String repositoryId, Properties properties, String folderId, ContentStream contentStream, VersioningState versioningState, List<String> policies, ExtensionsData extension) { authenticate(); String res; if (contentStream == null) { log.error("Tried to sync a folder !"); res = CmisConstants.tagRoot; } else if (folderId.equals(CmisConstants.tagDocument)) { res = documentEntryCmisService.create(repositoryId, properties, folderId, contentStream, versioningState, policies, extension); } else if (folderId.startsWith(CmisConstants.tagThreadChildren)) { res = threadEntryCmisService.create(repositoryId, properties, folderId, contentStream, versioningState, policies, extension); } else throw new CmisNotSupportedException(); return res; } @Override public void setContentStream(String repositoryId, Holder<String> objectId, Boolean overwriteFlag, Holder<String> changeToken, ContentStream contentStream, ExtensionsData extension) { authenticate(); if (objectId.getValue().startsWith(CmisConstants.tagDocumentEntry)) { documentEntryCmisService.setContentStream(repositoryId, objectId, overwriteFlag, changeToken, contentStream, extension); } else throw new CmisPermissionDeniedException(); } @Override public void updateProperties(String repositoryId, Holder<String> objectId, Holder<String> changeToken, Properties properties, ExtensionsData extension) { authenticate(); if (objectId.getValue().startsWith(CmisConstants.tagDocumentEntry)) { documentEntryCmisService.updateProperties(repositoryId, objectId, changeToken, properties, extension); } else if (objectId.getValue().startsWith(CmisConstants.tagThreadEntry)) { threadEntryCmisService.updateProperties(repositoryId, objectId, changeToken, properties, extension); } else throw new CmisPermissionDeniedException(); } @Override public FailedToDeleteData deleteTree(String repositoryId, String folderId, Boolean allVersions, UnfileObject unfileObjects, Boolean continueOnFailure, ExtensionsData extension) { authenticate(); FailedToDeleteData failedToDeleteData = new FailedToDeleteDataImpl(); return failedToDeleteData; } }