/** * 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.ssh.shell; import java.io.IOException; import java.util.LinkedList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.structr.common.Permission; import org.structr.common.error.FrameworkException; import org.structr.core.app.App; import org.structr.core.app.StructrApp; import org.structr.core.graph.Tx; import org.structr.files.ssh.StructrShellCommand; import org.structr.web.entity.AbstractFile; import org.structr.web.entity.Folder; import org.structr.web.entity.User; /** * * */ public class CdCommand extends NonInteractiveShellCommand { private static final Logger logger = LoggerFactory.getLogger(CdCommand.class.getName()); private String target = null; @Override public void execute(final StructrShellCommand parent) throws IOException { final App app = StructrApp.getInstance(); final Folder currentFolder = parent.getCurrentFolder(); try (final Tx tx = app.tx()) { if (target != null) { switch (target) { case "..": if (currentFolder != null) { final Folder parentFolder = currentFolder.getProperty(AbstractFile.parent); if (parentFolder != null) { if (parent.isAllowed(parentFolder, Permission.read, true)) { parent.setCurrentFolder(parentFolder); } } else { parent.setCurrentFolder(null); } } break; case ".": break; case "/": parent.setCurrentFolder(null); break; case "~": parent.setCurrentFolder(user.getProperty(User.homeDirectory)); break; default: setFolder(parent, currentFolder, target); break; } } else { parent.setCurrentFolder(user.getProperty(User.homeDirectory)); } tx.success(); } catch (FrameworkException fex) { logger.warn("", fex); } } @Override public void setCommand(final String command) throws IOException { if (command.contains(" ") && command.length() > 3) { target = command.substring(command.indexOf(" ") + 1); if (target.startsWith("\"")) { if (target.endsWith("\"")) { target = target.substring(1, target.length() - 2); } else { term.print("Unmatched quotes"); } } // remove trailing slash if (target != null && target.endsWith("/") && target.length() > 1) { target = target.substring(0, target.length() - 1); } } } @Override public void handleTabCompletion(final StructrShellCommand parent, final String line, final int tabCount) throws IOException { if (line.contains(" ") && line.length() >= 3) { String incompletePath = line.substring(line.indexOf(" ") + 1); Folder baseFolder = null; String lastPathPart = null; if (incompletePath.startsWith("\"")) { incompletePath = incompletePath.substring(1); } final App app = StructrApp.getInstance(); if ("..".equals(incompletePath)) { term.handleCharacter('/'); return; } if (incompletePath.startsWith("/")) { incompletePath = incompletePath.substring(1); } else { baseFolder = parent.getCurrentFolder(); } // identify full path parts and find folders final String[] parts = incompletePath.split("[/]+"); final int partCount = parts.length; try (final Tx tx = app.tx()) { // only a single path part if (partCount == 1) { lastPathPart = parts[0]; } else { lastPathPart = parts[partCount-1]; // more than a single path part, find preceding folders for (int i=0; i<partCount-1; i++) { // skip empty path parts if (StringUtils.isNotBlank(parts[i])) { baseFolder = app.nodeQuery(Folder.class).and(AbstractFile.parent, baseFolder).and(Folder.name, parts[i]).getFirst(); if (baseFolder == null) { return; } } } } final List<Folder> allFolders = app.nodeQuery(Folder.class).and(AbstractFile.parent, baseFolder).getAsList(); final List<Folder> folders = new LinkedList<>(); for (final Folder folder : allFolders) { if (folder.getName().startsWith(lastPathPart)) { folders.add(folder); } } if (folders.size() > 1) { // only display autocomplete suggestions after second tab if (tabCount > 1) { displayAutocompleteSuggestions(parent, folders, line); } } else if (!folders.isEmpty()) { final Folder folder = folders.get(0); if (parent.isAllowed(folder, Permission.read, false)) { if (lastPathPart.equals(folder.getName())) { // only display autocomplete suggestions after second tab if (tabCount > 1) { displayAutocompleteSuggestions(parent, folder.getProperty(Folder.folders), line); } else { if (!line.endsWith("/")) { term.handleCharacter('/'); } } } else { displayAutocompleteFolder(folder, lastPathPart); } } } tx.success(); } catch (FrameworkException fex) { logger.warn("", fex); } } } // ----- private methods ----- private void setFolder(final StructrShellCommand parent, final Folder currentFolder, final String targetFolderName) throws IOException, FrameworkException { final App app = StructrApp.getInstance(); String target = targetFolderName; // remove trailing slash if (target.endsWith("/")) { target = target.substring(0, target.length() - 1); } if (target.startsWith("/")) { final Folder folder = app.nodeQuery(Folder.class).and(Folder.path, target).getFirst(); if (folder != null) { if (parent.isAllowed(folder, Permission.read, true)) { parent.setCurrentFolder(folder); } else { term.println("Permission denied"); } } else { term.println("Folder " + target + " does not exist"); } } else { final Folder newFolder = parent.findRelativeFolder(currentFolder, target); if (newFolder == null) { term.println("Folder " + target + " does not exist"); } else { if (parent.isAllowed(newFolder, Permission.read, true)) { parent.setCurrentFolder(newFolder); } else { term.println("Permission denied"); } } } } private void displayAutocompleteFolder(final Folder folder, final String part) throws IOException { final String name = folder.getName(); if (name.startsWith(part)) { final String remainder = folder.getName().substring(part.length()); if (StringUtils.isNotEmpty(remainder)) { term.handleString(remainder); term.handleCharacter('/'); } } } private void displayAutocompleteSuggestions(final StructrShellCommand parent, final List<Folder> folders, final String line) throws IOException { if (!folders.isEmpty()) { final StringBuilder buf = new StringBuilder(); for (final Folder folder : folders) { if (parent.isAllowed(folder, Permission.read, false)) { buf.append(folder.getName()).append("/ "); } } if (buf.length() > 0) { term.println(); term.print(buf.toString()); term.println(); parent.displayPrompt(); term.print(line); } } } }