/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. */ package com.liferay.sync.internal.servlet; import com.liferay.document.library.kernel.exception.DuplicateFileException; import com.liferay.document.library.kernel.exception.NoSuchFileEntryException; import com.liferay.document.library.kernel.exception.NoSuchFileVersionException; import com.liferay.document.library.kernel.model.DLFileVersion; import com.liferay.document.library.kernel.service.DLAppService; import com.liferay.document.library.kernel.service.DLFileEntryLocalService; import com.liferay.document.library.kernel.service.DLFileVersionLocalService; import com.liferay.portal.kernel.exception.NoSuchImageException; import com.liferay.portal.kernel.json.JSONArray; import com.liferay.portal.kernel.json.JSONFactoryUtil; import com.liferay.portal.kernel.json.JSONObject; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.Group; import com.liferay.portal.kernel.model.Image; import com.liferay.portal.kernel.model.ImageConstants; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.portletfilerepository.PortletFileRepositoryUtil; import com.liferay.portal.kernel.repository.model.FileEntry; import com.liferay.portal.kernel.repository.model.FileVersion; import com.liferay.portal.kernel.repository.model.Folder; import com.liferay.portal.kernel.security.permission.PermissionChecker; import com.liferay.portal.kernel.security.permission.PermissionCheckerFactoryUtil; import com.liferay.portal.kernel.security.permission.PermissionThreadLocal; import com.liferay.portal.kernel.service.GroupLocalService; import com.liferay.portal.kernel.service.ImageLocalService; import com.liferay.portal.kernel.service.UserLocalService; import com.liferay.portal.kernel.servlet.HttpHeaders; import com.liferay.portal.kernel.servlet.PortalSessionThreadLocal; import com.liferay.portal.kernel.servlet.ServletResponseUtil; import com.liferay.portal.kernel.util.CharPool; import com.liferay.portal.kernel.util.FileUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.Http; import com.liferay.portal.kernel.util.MimeTypesUtil; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.kernel.util.Portal; import com.liferay.portal.kernel.util.StreamUtil; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.zip.ZipWriter; import com.liferay.portal.kernel.zip.ZipWriterFactoryUtil; import com.liferay.sync.SyncSiteUnavailableException; import com.liferay.sync.model.SyncDLFileVersionDiff; import com.liferay.sync.model.SyncDevice; import com.liferay.sync.service.SyncDLFileVersionDiffLocalServiceUtil; import com.liferay.sync.service.SyncDeviceLocalServiceUtil; import com.liferay.sync.service.configuration.SyncServiceConfigurationValues; import com.liferay.sync.util.SyncUtil; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; /** * @author Dennis Ju */ @Component( immediate = true, property = { "osgi.http.whiteboard.context.path=/sync", "osgi.http.whiteboard.servlet.name=com.liferay.sync.internal.servlet.SyncDownloadServlet", "osgi.http.whiteboard.servlet.pattern=/sync/download/*" }, service = Servlet.class ) public class SyncDownloadServlet extends HttpServlet { @Override public void service( HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { HttpSession session = request.getSession(); if (PortalSessionThreadLocal.getHttpSession() == null) { PortalSessionThreadLocal.setHttpSession(session); } User user = _portal.getUser(request); String syncUuid = request.getHeader("Sync-UUID"); if (syncUuid != null) { SyncDevice syncDevice = SyncDeviceLocalServiceUtil. fetchSyncDeviceByUuidAndCompanyId( syncUuid, user.getCompanyId()); if (syncDevice != null) { syncDevice.checkStatus(); } } PermissionChecker permissionChecker = PermissionCheckerFactoryUtil.create(user); PermissionThreadLocal.setPermissionChecker(permissionChecker); String path = _http.fixPath(request.getPathInfo()); String[] pathArray = StringUtil.split(path, CharPool.SLASH); if (pathArray[0].equals("image")) { long imageId = GetterUtil.getLong(pathArray[1]); sendImage(request, response, imageId); } else if (pathArray[0].equals("zip")) { String zipFileIds = ParamUtil.get( request, "zipFileIds", StringPool.BLANK); if (Validator.isNull(zipFileIds)) { throw new IllegalArgumentException( "Missing parameter zipFileIds"); } JSONArray zipFileIdsJSONArray = JSONFactoryUtil.createJSONArray( zipFileIds); sendZipFile(response, user.getUserId(), zipFileIdsJSONArray); } else if (pathArray[0].equals("zipfolder")) { long repositoryId = ParamUtil.getLong(request, "repositoryId"); long folderId = ParamUtil.getLong(request, "folderId"); if (repositoryId == 0) { throw new IllegalArgumentException( "Missing parameter repositoryId"); } else if (folderId == 0) { throw new IllegalArgumentException( "Missing parameter folderId"); } sendZipFolder( response, user.getUserId(), repositoryId, folderId); } else { long groupId = GetterUtil.getLong(pathArray[0]); String fileUuid = pathArray[1]; Group group = _groupLocalService.fetchGroup(groupId); if ((group == null) || !SyncUtil.isSyncEnabled(group)) { response.setHeader( _ERROR_HEADER, SyncSiteUnavailableException.class.getName()); ServletResponseUtil.write(response, new byte[0]); return; } boolean patch = ParamUtil.getBoolean(request, "patch"); if (patch) { sendPatch( request, response, user.getUserId(), groupId, fileUuid); } else { sendFile( request, response, user.getUserId(), groupId, fileUuid); } } } catch (NoSuchFileEntryException nsfee) { _portal.sendError( HttpServletResponse.SC_NOT_FOUND, nsfee, request, response); } catch (NoSuchFileVersionException nsfve) { _portal.sendError( HttpServletResponse.SC_NOT_FOUND, nsfve, request, response); } catch (Exception e) { _portal.sendError(e, request, response); } } protected void addZipFolderEntry( long userId, long repositoryId, long folderId, String folderPath, ZipWriter zipWriter) throws Exception { List<FileEntry> fileEntries = _dlAppService.getFileEntries( repositoryId, folderId); for (FileEntry fileEntry : fileEntries) { InputStream inputStream = null; try { inputStream = _dlFileEntryLocalService.getFileAsStream( userId, fileEntry.getFileEntryId(), fileEntry.getVersion(), false); String filePath = folderPath + fileEntry.getTitle(); zipWriter.addEntry(filePath, inputStream); } finally { StreamUtil.cleanUp(inputStream); } } List<Folder> childFolders = _dlAppService.getFolders( repositoryId, folderId); for (Folder childFolder : childFolders) { String childFolderPath = folderPath + childFolder.getName() + StringPool.FORWARD_SLASH; addZipFolderEntry( userId, repositoryId, childFolder.getFolderId(), childFolderPath, zipWriter); } } protected File getDeltaFile( long userId, long fileEntryId, long sourceVersionId, long targetVersionId) throws Exception { DLFileVersion sourceDLFileVersion = _dlFileVersionLocalService.getDLFileVersion(sourceVersionId); File sourceFile = _dlFileEntryLocalService.getFile( userId, fileEntryId, sourceDLFileVersion.getVersion(), false); DLFileVersion targetDLFileVersion = _dlFileVersionLocalService.getDLFileVersion(targetVersionId); File targetFile = _dlFileEntryLocalService.getFile( userId, fileEntryId, targetDLFileVersion.getVersion(), false); return SyncUtil.getFileDelta(sourceFile, targetFile); } protected DownloadServletInputStream getFileDownloadServletInputStream( long userId, long groupId, String uuid, String version, long versionId) throws Exception { FileEntry fileEntry = _dlAppService.getFileEntryByUuidAndGroupId( uuid, groupId); if (fileEntry.isInTrash()) { throw new NoSuchFileEntryException(); } if (Validator.isNull(version)) { InputStream inputStream = _dlFileEntryLocalService.getFileAsStream( userId, fileEntry.getFileEntryId(), fileEntry.getVersion(), false); return new DownloadServletInputStream( inputStream, fileEntry.getFileName(), fileEntry.getMimeType(), fileEntry.getSize()); } else { if (versionId > 0) { DLFileVersion dlFileVersion = _dlFileVersionLocalService.fetchDLFileVersion(versionId); return new DownloadServletInputStream( dlFileVersion.getContentStream(false), dlFileVersion.getFileName(), dlFileVersion.getMimeType(), dlFileVersion.getSize()); } else { FileVersion fileVersion = fileEntry.getFileVersion(version); return new DownloadServletInputStream( fileVersion.getContentStream(false), fileVersion.getFileName(), fileVersion.getMimeType(), fileVersion.getSize()); } } } protected DownloadServletInputStream getPatchDownloadServletInputStream( long userId, long groupId, String uuid, long sourceVersionId, long targetVersionId) throws Exception { FileEntry fileEntry = _dlAppService.getFileEntryByUuidAndGroupId( uuid, groupId); if (fileEntry.isInTrash()) { throw new NoSuchFileEntryException(); } if (!SyncServiceConfigurationValues.SYNC_FILE_DIFF_CACHE_ENABLED) { File deltaFile = null; try { deltaFile = getDeltaFile( userId, fileEntry.getFileEntryId(), sourceVersionId, targetVersionId); return new DownloadServletInputStream( new FileInputStream(deltaFile), deltaFile.length()); } finally { FileUtil.delete(deltaFile); } } SyncDLFileVersionDiff syncDLFileVersionDiff = SyncDLFileVersionDiffLocalServiceUtil.fetchSyncDLFileVersionDiff( fileEntry.getFileEntryId(), sourceVersionId, targetVersionId); if (syncDLFileVersionDiff != null) { SyncDLFileVersionDiffLocalServiceUtil.refreshExpirationDate( syncDLFileVersionDiff.getSyncDLFileVersionDiffId()); FileEntry dataFileEntry = PortletFileRepositoryUtil.getPortletFileEntry( syncDLFileVersionDiff.getDataFileEntryId()); return new DownloadServletInputStream( dataFileEntry.getContentStream(), dataFileEntry.getSize()); } else { File deltaFile = null; try { deltaFile = getDeltaFile( userId, fileEntry.getFileEntryId(), sourceVersionId, targetVersionId); try { SyncDLFileVersionDiffLocalServiceUtil. addSyncDLFileVersionDiff( fileEntry.getFileEntryId(), sourceVersionId, targetVersionId, deltaFile); } catch (DuplicateFileException dfe) { // LPS-52675 if (_log.isDebugEnabled()) { _log.debug(dfe, dfe); } } return new DownloadServletInputStream( new FileInputStream(deltaFile), deltaFile.length()); } finally { FileUtil.delete(deltaFile); } } } protected void processException( String zipFileId, String exception, JSONObject errorsJSONObject) { JSONObject exceptionJSONObject = JSONFactoryUtil.createJSONObject(); exceptionJSONObject.put("exception", exception); errorsJSONObject.put(zipFileId, exceptionJSONObject); } protected void sendFile( HttpServletRequest request, HttpServletResponse response, long userId, long groupId, String uuid) throws Exception { String version = ParamUtil.getString(request, "version"); long versionId = ParamUtil.getLong(request, "versionId"); DownloadServletInputStream downloadServletInputStream = getFileDownloadServletInputStream( userId, groupId, uuid, version, versionId); if (request.getHeader(HttpHeaders.RANGE) != null) { ServletResponseUtil.sendFileWithRangeHeader( request, response, downloadServletInputStream.getFileName(), downloadServletInputStream, downloadServletInputStream.getSize(), downloadServletInputStream.getMimeType()); } else { ServletResponseUtil.write( response, downloadServletInputStream, downloadServletInputStream.getSize()); } } protected void sendImage( HttpServletRequest request, HttpServletResponse response, long imageId) throws Exception { User user = _userLocalService.fetchUser(imageId); if (user != null) { imageId = user.getPortraitId(); } Image image = _imageLocalService.fetchImage(imageId); if (image == null) { _portal.sendError( HttpServletResponse.SC_NOT_FOUND, new NoSuchImageException(), request, response); return; } String type = image.getType(); if (!type.equals(ImageConstants.TYPE_NOT_AVAILABLE)) { String contentType = MimeTypesUtil.getExtensionContentType(type); response.setContentType(contentType); } ServletResponseUtil.write(response, image.getTextObj()); } protected void sendPatch( HttpServletRequest request, HttpServletResponse response, long userId, long groupId, String uuid) throws Exception { long sourceVersionId = ParamUtil.getLong(request, "sourceVersionId", 0); long targetVersionId = ParamUtil.getLong(request, "targetVersionId", 0); DownloadServletInputStream downloadServletInputStream = getPatchDownloadServletInputStream( userId, groupId, uuid, sourceVersionId, targetVersionId); ServletResponseUtil.write( response, downloadServletInputStream, downloadServletInputStream.getSize()); } protected void sendZipFile( HttpServletResponse response, long userId, JSONArray zipFileIdsJSONArray) throws Exception { ZipWriter zipWriter = ZipWriterFactoryUtil.getZipWriter(); JSONObject errorsJSONObject = JSONFactoryUtil.createJSONObject(); for (int i = 0; i < zipFileIdsJSONArray.length(); i++) { JSONObject zipObjectJSONObject = zipFileIdsJSONArray.getJSONObject( i); long groupId = zipObjectJSONObject.getLong("groupId"); String zipFileId = zipObjectJSONObject.getString("zipFileId"); Group group = _groupLocalService.fetchGroup(groupId); if ((group == null) || !SyncUtil.isSyncEnabled(group)) { processException( zipFileId, SyncSiteUnavailableException.class.getName(), errorsJSONObject); continue; } InputStream inputStream = null; try { String uuid = zipObjectJSONObject.getString("uuid"); if (zipObjectJSONObject.getBoolean("patch")) { long sourceVersionId = zipObjectJSONObject.getLong( "sourceVersionId", 0); long targetVersionId = zipObjectJSONObject.getLong( "targetVersionId", 0); inputStream = getPatchDownloadServletInputStream( userId, groupId, uuid, sourceVersionId, targetVersionId); zipWriter.addEntry(zipFileId, inputStream); } else { inputStream = getFileDownloadServletInputStream( userId, groupId, uuid, zipObjectJSONObject.getString("version"), zipObjectJSONObject.getLong("versionId")); zipWriter.addEntry(zipFileId, inputStream); } } catch (Exception e) { Class<?> clazz = e.getClass(); processException(zipFileId, clazz.getName(), errorsJSONObject); } finally { StreamUtil.cleanUp(inputStream); } } zipWriter.addEntry("errors.json", errorsJSONObject.toString()); File file = zipWriter.getFile(); ServletResponseUtil.write( response, new FileInputStream(file), file.length()); } protected void sendZipFolder( HttpServletResponse response, long userId, long repositoryId, long folderId) throws Exception { ZipWriter zipWriter = ZipWriterFactoryUtil.getZipWriter(); addZipFolderEntry( userId, repositoryId, folderId, StringPool.BLANK, zipWriter); File file = zipWriter.getFile(); ServletResponseUtil.write( response, new FileInputStream(file), file.length()); } @Reference(unbind = "-") protected void setDLAppService(DLAppService dlAppService) { _dlAppService = dlAppService; } @Reference(unbind = "-") protected void setDlFileEntryLocalService( DLFileEntryLocalService dlFileEntryLocalService) { _dlFileEntryLocalService = dlFileEntryLocalService; } @Reference(unbind = "-") protected void setDlFileVersionLocalService( DLFileVersionLocalService dlFileVersionLocalService) { _dlFileVersionLocalService = dlFileVersionLocalService; } @Reference(unbind = "-") protected void setGroupLocalService(GroupLocalService groupLocalService) { _groupLocalService = groupLocalService; } @Reference(unbind = "-") protected void setImageLocalService(ImageLocalService imageLocalService) { _imageLocalService = imageLocalService; } @Reference(unbind = "-") protected void setUserLocalService(UserLocalService userLocalService) { _userLocalService = userLocalService; } private static final String _ERROR_HEADER = "Sync-Error"; private static final Log _log = LogFactoryUtil.getLog( SyncDownloadServlet.class); private DLAppService _dlAppService; private DLFileEntryLocalService _dlFileEntryLocalService; private DLFileVersionLocalService _dlFileVersionLocalService; private GroupLocalService _groupLocalService; @Reference private Http _http; private ImageLocalService _imageLocalService; @Reference private Portal _portal; private UserLocalService _userLocalService; }