/**
* 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.ftp;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.SftpException;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.Vector;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.fail;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.common.error.FrameworkException;
import org.structr.core.graph.Tx;
import org.structr.web.common.SSHTest;
import org.structr.web.entity.FileBase;
import org.structr.web.entity.Folder;
/**
* Tests for the SSH file interface.
*
*
*/
public class SSHFilesTest extends SSHTest {
private static final Logger logger = LoggerFactory.getLogger(SSHFilesTest.class.getName());
@Test
public void test00RootDirectoriesAndAttributes() {
final ChannelSftp sftp = setupSftpClient("ftpuser1", "ftpuserpw1");
try {
final Vector<LsEntry> entries = sftp.ls("/");
// listing contains "." => 5 entries
assertEquals("Invalid result size for SSH root directory", 5, entries.size());
final LsEntry currentDir = entries.get(0);
final LsEntry files = entries.get(1);
final LsEntry pages = entries.get(2);
final LsEntry schema = entries.get(3);
final LsEntry components = entries.get(4);
// check names
assertEquals("Invalid current directory name", ".", currentDir.getFilename());
assertEquals("Invalid files directory name", "files", files.getFilename());
assertEquals("Invalid pages directory name", "pages", pages.getFilename());
assertEquals("Invalid schema directory name", "schema", schema.getFilename());
assertEquals("Invalid components directory name", "components", components.getFilename());
// check permissions
assertEquals("Invalid permissions on . directory", "dr--------", currentDir.getAttrs().getPermissionsString());
assertEquals("Invalid permissions on files directory", "drwxrwxr-x", files.getAttrs().getPermissionsString());
assertEquals("Invalid permissions on pages directory", "drwxrwxr-x", pages.getAttrs().getPermissionsString());
assertEquals("Invalid permissions on schema directory", "drwxrwxr-x", schema.getAttrs().getPermissionsString());
assertEquals("Invalid permissions on components directory", "drwxrwxr-x", components.getAttrs().getPermissionsString());
// check flags (?)
assertEquals("Invalid flags on . directory", 12, currentDir.getAttrs().getFlags());
assertEquals("Invalid flags on files directory", 12, files.getAttrs().getFlags());
assertEquals("Invalid flags on pages directory", 12, pages.getAttrs().getFlags());
assertEquals("Invalid flags on schema directory", 12, schema.getAttrs().getFlags());
assertEquals("Invalid flags on components directory", 12, components.getAttrs().getFlags());
// check size
assertEquals("Invalid size on . directory", 0, currentDir.getAttrs().getSize());
assertEquals("Invalid size on files directory", 0, files.getAttrs().getSize());
assertEquals("Invalid size on pages directory", 0, pages.getAttrs().getSize());
assertEquals("Invalid size on schema directory", 0, schema.getAttrs().getSize());
assertEquals("Invalid size on components directory", 0, components.getAttrs().getSize());
final String date = getDateStringDependingOnCurrentDayOfMonth();
// check string representation
assertEquals("Invalid string representation of . directory", "dr-------- 1 superadmin superadmin 0 " + date + " .", currentDir.getLongname());
assertEquals("Invalid string representation of files directory", "drwxrwxr-x 1 superadmin superadmin 0 " + date + " files", files.getLongname());
assertEquals("Invalid string representation of pages directory", "drwxrwxr-x 1 superadmin superadmin 0 " + date + " pages", pages.getLongname());
assertEquals("Invalid string representation of schema directory", "drwxrwxr-x 1 superadmin superadmin 0 " + date + " schema", schema.getLongname());
assertEquals("Invalid string representation of components directory","drwxrwxr-x 1 superadmin superadmin 0 " + date + " components", components.getLongname());
sftp.disconnect();
} catch (SftpException ex) {
logger.warn("", ex);
fail("Unexpected exception: " + ex.getMessage());
}
}
@Test
public void test00StoreFile() {
ChannelSftp sftp = setupSftpClient("ftpuser1", "ftpuserpw1");
final String testContent1 = "Test Content öäü";
final String testContent2 = "Test Content 2";
final String name1 = "file1.txt";
final String name2 = "fileöäüß.txt";
try {
final Vector files = sftp.ls("/files");
assertNotNull(files);
assertEquals(2, files.size());
try (final OutputStream os = sftp.put("/files/" + name1)) {
IOUtils.write(testContent1, os);
os.flush();
} catch (IOException ioex) {
ioex.printStackTrace();
}
try (final OutputStream os = sftp.put("/files/" + name2)) {
IOUtils.write(testContent2, os);
os.flush();
} catch (IOException ioex) {
ioex.printStackTrace();
}
} catch (SftpException ex) {
logger.warn("", ex);
fail("Unexpected exception: " + ex.getMessage());
}
try (final Tx tx = app.tx()) {
final List<FileBase> files = app.nodeQuery(FileBase.class).getAsList();
assertEquals("Invalid number of test files", 2, files.size());
final FileBase file1 = files.get(0);
final FileBase file2 = files.get(1);
assertEquals("Invalid test file name", name1, file1.getName());
assertEquals("Invalid test file name", name2, file2.getName());
try {
assertEquals("Invalid test file content", testContent1, IOUtils.toString(file1.getInputStream()));
assertEquals("Invalid test file content", testContent2, IOUtils.toString(file2.getInputStream()));
} catch (IOException ioex) {
fail("Unexpected exception: " + ioex.getMessage());
}
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception: " + fex.getMessage());
}
try {
final Vector<LsEntry> entries = sftp.ls("/files");
// listing contains "." and ".." => 4 entries
assertEquals("Invalid result size for directory", 4, entries.size());
final LsEntry file1 = entries.get(2);
final LsEntry file2 = entries.get(3);
// check names
assertEquals("Invalid test file name", name1, file1.getFilename());
assertEquals("Invalid test file name", name2, file2.getFilename());
// check permissions
assertEquals("Invalid permissions on test file", "-rw-------", file1.getAttrs().getPermissionsString());
assertEquals("Invalid permissions on test file", "-rw-------", file2.getAttrs().getPermissionsString());
// check flags (?)
assertEquals("Invalid flags on test file", 13, file1.getAttrs().getFlags());
assertEquals("Invalid flags on test file", 13, file2.getAttrs().getFlags());
// check size
assertEquals("Invalid test file size", 19, file1.getAttrs().getSize());
assertEquals("Invalid test file size", 14, file2.getAttrs().getSize());
final String date = getDateStringDependingOnCurrentDayOfMonth();
// check string representation
assertEquals("Invalid string representation of test file", "-rw------- 1 ftpuser1 ftpuser1 19 " + date + " " + name1, file1.getLongname());
assertEquals("Invalid string representation of test file", "-rw------- 1 ftpuser1 ftpuser1 14 " + date + " " + name2, file2.getLongname());
sftp.disconnect();
} catch (SftpException ex) {
ex.printStackTrace();
fail("Unexpected exception: " + ex.getMessage());
}
}
@Test
public void test02RenameFile() {
ChannelSftp sftp = setupSftpClient("ftpuser1", "ftpuserpw1");
final String testContent1 = "Test Content öäü";
final String name1 = "file1.txt";
final String name2 = "fileöäüß.txt";
try {
try (final OutputStream os = sftp.put("/files/" + name1)) {
IOUtils.write(testContent1, os);
os.flush();
} catch (IOException ioex) {
ioex.printStackTrace();
}
sftp.rename("/files/" + name1, "/files/" + name2);
} catch (SftpException ex) {
logger.warn("", ex);
fail("Unexpected exception: " + ex.getMessage());
}
try {
final String date = getDateStringDependingOnCurrentDayOfMonth();
final Vector<LsEntry> entries = sftp.ls("/files");
// listing contains "." and ".." => 3 entries
assertEquals("Invalid result size for directory", 3, entries.size());
final LsEntry file = entries.get(2);
// check attributes
assertEquals("Invalid test file name", name2, file.getFilename());
assertEquals("Invalid permissions on test file", "-rw-------", file.getAttrs().getPermissionsString());
assertEquals("Invalid flags on test file", 13, file.getAttrs().getFlags());
assertEquals("Invalid test file size", 19, file.getAttrs().getSize());
assertEquals("Invalid string representation of test file", "-rw------- 1 ftpuser1 ftpuser1 19 " + date + " " + name2, file.getLongname());
sftp.disconnect();
} catch (SftpException ex) {
ex.printStackTrace();
fail("Unexpected exception: " + ex.getMessage());
}
}
@Test
public void test03MoveFile() {
ChannelSftp sftp = setupSftpClient("ftpuser1", "ftpuserpw1");
final String testContent1 = "Test Content öäü";
final String name1 = "file1.txt";
final String name2 = "fileöäüß.txt";
try {
sftp.mkdir("/files/dir1");
sftp.mkdir("/files/dir2");
try (final OutputStream os = sftp.put("/files/dir1/" + name1)) {
IOUtils.write(testContent1, os);
os.flush();
} catch (IOException ioex) {
ioex.printStackTrace();
}
sftp.rename("/files/dir1/" + name1, "/files/dir2/" + name2);
} catch (SftpException ex) {
logger.warn("", ex);
fail("Unexpected exception: " + ex.getMessage());
}
try {
final String date = getDateStringDependingOnCurrentDayOfMonth();
final Vector<LsEntry> entries = sftp.ls("/files/dir2");
// listing contains "." and ".." => 3 entries
assertEquals("Invalid result size for directory", 3, entries.size());
final LsEntry file = entries.get(2);
// check attributes
assertEquals("Invalid test file name", name2, file.getFilename());
assertEquals("Invalid permissions on test file", "-rw-------", file.getAttrs().getPermissionsString());
assertEquals("Invalid flags on test file", 13, file.getAttrs().getFlags());
assertEquals("Invalid test file size", 19, file.getAttrs().getSize());
assertEquals("Invalid string representation of test file", "-rw------- 1 ftpuser1 ftpuser1 19 " + date + " " + name2, file.getLongname());
sftp.disconnect();
} catch (SftpException ex) {
ex.printStackTrace();
fail("Unexpected exception: " + ex.getMessage());
}
}
@Test
public void test04OverwriteFile() {
ChannelSftp sftp = setupSftpClient("ftpuser1", "ftpuserpw1");
final String testContent1 = "Initial Content";
final String testContent2 = "Overwritten Content";
final String name = "file1.txt";
try {
final Vector files = sftp.ls("/files");
assertNotNull(files);
// listing contains "." and ".." => 3 entries
assertEquals(2, files.size());
try (final OutputStream os = sftp.put("/files/" + name)) {
IOUtils.write(testContent1, os);
os.flush();
} catch (IOException ioex) {
ioex.printStackTrace();
}
try (final OutputStream os = sftp.put("/files/" + name)) {
IOUtils.write(testContent2, os);
os.flush();
} catch (IOException ioex) {
ioex.printStackTrace();
}
} catch (SftpException ex) {
logger.warn("", ex);
fail("Unexpected exception: " + ex.getMessage());
}
try (final Tx tx = app.tx()) {
final List<FileBase> files = app.nodeQuery(FileBase.class).getAsList();
assertEquals("Invalid number of test files", 1, files.size());
final FileBase file1 = files.get(0);
assertEquals("Invalid test file name", name, file1.getName());
try {
assertEquals("Invalid test file content", testContent2, IOUtils.toString(file1.getInputStream()));
} catch (IOException ioex) {
fail("Unexpected exception: " + ioex.getMessage());
}
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception: " + fex.getMessage());
}
try {
final Vector<LsEntry> entries = sftp.ls("/files");
// listing contains "." and ".." => 3 entries
assertEquals("Invalid result size for directory", 3, entries.size());
final LsEntry file1 = entries.get(2);
// check names
assertEquals("Invalid test file name", name, file1.getFilename());
// check permissions
assertEquals("Invalid permissions on test file", "-rw-------", file1.getAttrs().getPermissionsString());
// check flags (?)
assertEquals("Invalid flags on test file", 13, file1.getAttrs().getFlags());
// check size
assertEquals("Invalid test file size", 19, file1.getAttrs().getSize());
final String date = getDateStringDependingOnCurrentDayOfMonth();
// check string representation
assertEquals("Invalid string representation of test file", "-rw------- 1 ftpuser1 ftpuser1 19 " + date + " " + name, file1.getLongname());
sftp.disconnect();
} catch (SftpException ex) {
ex.printStackTrace();
fail("Unexpected exception: " + ex.getMessage());
}
}
@Test
public void test05DeleteFile() {
final ChannelSftp sftp = setupSftpClient("ftpuser1", "ftpuserpw1");
final String testContent = "Test Content öäü";
final String name = "file1.txt";
try {
final Vector files = sftp.ls("/files");
assertNotNull(files);
assertEquals(2, files.size());
try (final OutputStream os = sftp.put("/files/" + name)) {
IOUtils.write(testContent, os);
os.flush();
} catch (IOException ioex) {
ioex.printStackTrace();
}
} catch (SftpException ex) {
logger.warn("", ex);
fail("Unexpected exception: " + ex.getMessage());
}
try (final Tx tx = app.tx()) {
final List<FileBase> files = app.nodeQuery(FileBase.class).getAsList();
assertEquals("Invalid number of test files", 1, files.size());
final FileBase file1 = files.get(0);
assertEquals("Invalid test file name", name, file1.getName());
try {
assertEquals("Invalid test file content", testContent, IOUtils.toString(file1.getInputStream()));
} catch (IOException ioex) {
fail("Unexpected exception: " + ioex.getMessage());
}
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception: " + fex.getMessage());
}
try {
sftp.rm("/files/" + name);
sftp.disconnect();
} catch (SftpException ex) {
ex.printStackTrace();
fail("Unexpected exception: " + ex.getMessage());
}
try (final Tx tx = app.tx()) {
final List<FileBase> files = app.nodeQuery(FileBase.class).getAsList();
assertEquals("Invalid number of test files", 0, files.size());
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception: " + fex.getMessage());
}
}
@Test
public void test06DeleteDirectory() {
final ChannelSftp sftp = setupSftpClient("ftpuser1", "ftpuserpw1");
try {
// create some dirs
sftp.mkdir("/files/test1");
sftp.mkdir("/files/test2");
sftp.mkdir("/files/test2/nested1");
sftp.mkdir("/files/test2/nested1/nested2");
// delete one dir
sftp.rmdir("/files/test2/nested1/nested2");
// byebye
sftp.disconnect();
} catch (SftpException ex) {
fail("Unexpected exception: " + ex.getMessage());
}
try (final Tx tx = app.tx()) {
assertEquals("Folder test1 should exist", "test1", app.nodeQuery(Folder.class).andName("test1").getFirst().getName());
assertEquals("Folder test2 should exist", "test2", app.nodeQuery(Folder.class).andName("test2").getFirst().getName());
assertEquals("Folder nested1 should exist", "nested1", app.nodeQuery(Folder.class).andName("nested1").getFirst().getName());
assertNull("Folder nested2 should have been deleted", app.nodeQuery(Folder.class).andName("nested2").getFirst());
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception: " + fex.getMessage());
}
}
// ----- private methods -----
private String getDateStringDependingOnCurrentDayOfMonth() {
final Calendar cal = GregorianCalendar.getInstance();
if (cal.get(Calendar.DAY_OF_MONTH) < 10) {
return new SimpleDateFormat("MMM d HH:mm", Locale.ENGLISH).format(System.currentTimeMillis());
}
return new SimpleDateFormat("MMM dd HH:mm", Locale.ENGLISH).format(System.currentTimeMillis());
}
}