/**
* Copyright (C) 2010-2017 Structr GmbH
*
* This file is part of Structr <http://structr.org>.
*
* Structr 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.
*
* Structr 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
* along with Structr. If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.files.ftp;
import org.apache.commons.lang3.StringUtils;
import org.apache.ftpserver.ftplet.FileSystemView;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.FtpFile;
import org.apache.ftpserver.ftplet.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.common.AccessMode;
import org.structr.common.PathHelper;
import org.structr.common.SecurityContext;
import org.structr.common.error.FrameworkException;
import org.structr.core.app.StructrApp;
import org.structr.core.entity.AbstractUser;
import org.structr.core.graph.Tx;
import org.structr.rest.auth.AuthHelper;
import org.structr.web.common.FileHelper;
import org.structr.web.entity.AbstractFile;
import org.structr.web.entity.FileBase;
import org.structr.web.entity.Folder;
import org.structr.web.entity.dom.Page;
/**
*
*
*/
public class StructrFileSystemView implements FileSystemView {
private static final Logger logger = LoggerFactory.getLogger(StructrFileSystemView.class.getName());
private StructrFtpUser user = null;
private SecurityContext securityContext = null;
private String workingDir = "/";
public StructrFileSystemView(final User user) {
try (Tx tx = StructrApp.getInstance().tx()) {
org.structr.web.entity.User structrUser = (org.structr.web.entity.User) AuthHelper.getPrincipalForCredential(AbstractUser.name, user.getName());
securityContext = SecurityContext.getInstance(structrUser, AccessMode.Backend);
this.user = new StructrFtpUser(securityContext, structrUser);
tx.success();
} catch (FrameworkException fex) {
logger.error("Error while initializing file system view", fex);
}
}
@Override
public FtpFile getHomeDirectory() throws FtpException {
try (Tx tx = StructrApp.getInstance(securityContext).tx()) {
org.structr.web.entity.User structrUser = (org.structr.web.entity.User) AuthHelper.getPrincipalForCredential(AbstractUser.name, user.getName());
final Folder homeDir = structrUser.getProperty(org.structr.web.entity.User.homeDirectory);
tx.success();
return new StructrFtpFolder(securityContext, homeDir);
} catch (FrameworkException fex) {
logger.error("Error while getting home directory", fex);
}
return null;
}
@Override
public FtpFile getWorkingDirectory() throws FtpException {
try (Tx tx = StructrApp.getInstance(securityContext).tx()) {
AbstractFile structrWorkingDir = FileHelper.getFileByAbsolutePath(securityContext, workingDir);
tx.success();
if (structrWorkingDir == null || structrWorkingDir instanceof FileBase) {
return new StructrFtpFolder(securityContext, null);
}
return new StructrFtpFolder(securityContext, (Folder) structrWorkingDir);
} catch (FrameworkException fex) {
logger.error("Error in changeWorkingDirectory()", fex);
}
return null;
}
@Override
public boolean changeWorkingDirectory(String requestedPath) throws FtpException {
try (Tx tx = StructrApp.getInstance(securityContext).tx()) {
final StructrFtpFolder newWorkingDirectory = (StructrFtpFolder) getFile(requestedPath);
workingDir = newWorkingDirectory.getAbsolutePath();
tx.success();
return true;
} catch (FrameworkException fex) {
logger.error("Error in changeWorkingDirectory()", fex);
}
return false;
}
@Override
public FtpFile getFile(final String rawRequestedPath) throws FtpException {
String requestedPath = rawRequestedPath;
// remove trailing slash
if (requestedPath.endsWith("/")) {
requestedPath = requestedPath.substring(0, requestedPath.length() - 1);
}
logger.info("Requested path: {}, cleaned to {}", rawRequestedPath, requestedPath);
try (Tx tx = StructrApp.getInstance(securityContext).tx()) {
if (StringUtils.isBlank(requestedPath) || "/".equals(requestedPath)) {
return getHomeDirectory();
}
StructrFtpFolder cur = (StructrFtpFolder) getWorkingDirectory();
if (".".equals(requestedPath) || "./".equals(requestedPath)) {
return cur;
}
if ("..".equals(requestedPath) || "../".equals(requestedPath)) {
return new StructrFtpFolder(securityContext, cur.getStructrFile().getProperty(AbstractFile.parent));
}
// If relative path requested, prepend base path
if (!requestedPath.startsWith("/")) {
String basePath = cur.getAbsolutePath();
logger.info("Base path: {}", basePath);
while (requestedPath.startsWith("..")) {
requestedPath = StringUtils.stripStart(StringUtils.stripStart(requestedPath, ".."), "/");
basePath = StringUtils.substringBeforeLast(basePath, "/");
}
requestedPath = StringUtils.stripEnd(basePath.equals("/") ? "/".concat(requestedPath) : basePath.concat("/").concat(requestedPath), "/");
logger.info("Base path: {}, requestedPath: {}", new Object[]{basePath, requestedPath});
}
AbstractFile file = FileHelper.getFileByAbsolutePath(securityContext, requestedPath);
if (file != null) {
if (file instanceof Folder) {
tx.success();
return new StructrFtpFolder(securityContext, (Folder) file);
} else {
tx.success();
return new StructrFtpFile(securityContext, (FileBase) file);
}
}
// Look up a page by its name
Page page = StructrApp.getInstance(securityContext).nodeQuery(Page.class).andName(PathHelper.getName(requestedPath)).getFirst();
if (page != null) {
tx.success();
return new FtpFilePageWrapper(page);
}
logger.warn("No existing file found: {}", requestedPath);
tx.success();
return new FileOrFolder(requestedPath, user);
} catch (FrameworkException fex) {
logger.error("Error in getFile()", fex);
}
return null;
}
@Override
public boolean isRandomAccessible() throws FtpException {
logger.info("isRandomAccessible(), returning true");
return true;
}
@Override
public void dispose() {
logger.info("dispose() does nothing");
}
}