package Visual_DVM_2021.Passes.SSH; import Common.Constants; import Common.Utils.Utils; import Common.Utils.Validators.ShellParser; import GlobalData.Machine.Machine; import GlobalData.RemoteFile.RemoteFile; import GlobalData.User.User; import ProjectData.Project.db_project_info; import Visual_DVM_2021.Passes.PassException; import Visual_DVM_2021.Passes.Pass_2021; import com.jcraft.jsch.*; import javafx.util.Pair; import java.io.*; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Vector; //убираем лишний класс-прослойку. //старый server pass Пока не трогаем. //https://stackoverflow.com/questions/15108923/sftp-file-transfer-using-java-jsch //https://javadevblog.com/kak-dobavit-biblioteku-jar-fajl-v-proekt-intellij-idea.html //http://www.jcraft.com/jsch/ //http://developer-remarks.blogspot.com/2013/05/ssh-via-jsch-example.html //https://stackoverflow.com/questions/4194439/sending-commands-to-server-via-jsch-shell-channel public abstract class ConnectionPass extends Pass_2021 { //----------------------------------------------- public static final String modules = "modules"; //-------------- public static final String starter = "starter"; public static final String launcher = "launcher"; public static final String planner = "planner"; //-------------- public static final String Process_r_header = "Process_r.h"; public static final String starter_code = "starter.cpp"; public static final String launcher_code = "launcher.cpp"; //-------------- public static final String projects = "projects"; public static final String compilers = "compilers"; public static final String tests = "tests"; public Machine machine = null; public User user = null; //тут как в WinScp - 2 независимых канала. один для файлов. другой для команд. public ChannelSftp sftpChannel = null; public ChannelShell shellChannel = null; public ChannelExec execChannel = null; //---------------------------------------------------- protected JSch jsch; protected Session session = null; protected boolean needsInitialize() { return true; } //------------------------------------- protected boolean isConnected = false; //пока public ChannelSftp getSftpChannel() { return sftpChannel; } //учитывать. что пока сокет не создан, прервать соединение нельзя. public void Connect() throws Exception { isConnected = false; session = (jsch = new JSch()).getSession(user.login, machine.address, machine.port); switch (user.authentication) { case password: session.setPassword(user.password); break; /* пока не совсем ясно как это делать. case key: // https://stackoverflow.com/questions/47422025/java-sftp-authentication-with-ppk-file //todo перешифровать ключ https://www.example-code.com/java/ssh_ppk_to_pem.asp jsch.addIdentity(user.password); break; */ } session.setConfig("StrictHostKeyChecking", "no"); //UI.Print("Establishing Connection..."); ShowMessage1("соединение с машиной " + Utils.Brackets(machine.getURL()) + ".."); session.connect(0); //UI.Print("Connection established."); //UI.Print("Creating SFTP Channel."); sftpChannel = (ChannelSftp) session.openChannel("sftp"); sftpChannel.connect(); isConnected = true; // теперь можно прерывать метод. if (needsInitialize()) { RemoteFile userWorkspace = new RemoteFile(user.workspace, true); if (!Exists(userWorkspace)) throw new WorkspaceNotFoundException( "Рабочее пространство пользователя " + Utils.Brackets(user.login) + " на машине " + Utils.Brackets(machine.getURL()) + "\n" + Utils.Brackets(user.workspace) + "\nне найдено.\n" + "Требуется выполнить повторную инициализацию пользователя." ); } } public void Disconnect() { if (sftpChannel != null) sftpChannel.disconnect(); if (execChannel != null) execChannel.disconnect(); if (session != null) session.disconnect(); //---------------------- sftpChannel = null; execChannel = null; session = null; isConnected = false; //--------------------- // UI.Print(DebugPrintLevel.Session, "session ended"); } @Override protected void performFinish() throws Exception { Disconnect(); } public void Command(String... commands) throws Exception { if (commands.length > 0) { String command = String.join("\n", commands); // UI.Print(DebugPrintLevel.Session, command); // UI.Print(DebugPrintLevel.Session, "Creating Exec Channel."); execChannel = (ChannelExec) session.openChannel("exec"); execChannel.setErrStream(System.err); execChannel.setCommand(command); execChannel.connect(); BufferedReader in = new BufferedReader(new InputStreamReader(execChannel.getInputStream())); while (in.readLine() != null) ; execChannel.disconnect(); } } public String CommandWithAnswer(char end, String... commands) throws Exception { String output = ""; if (commands.length > 0) { String command = String.join("\n", commands); // UI.Print(DebugPrintLevel.Session, command); // UI.Print(DebugPrintLevel.Session, "Creating Exec Channel."); // System.out.println(Utils.Brackets(command)); execChannel = (ChannelExec) session.openChannel("exec"); execChannel.setErrStream(System.err); InputStreamReader reader = new InputStreamReader(execChannel.getInputStream()); execChannel.setCommand(command); execChannel.connect(); char[] chars = new char[1]; while (reader.read(chars) >= 0) if (chars[0] == end) break; else output += chars[0]; execChannel.disconnect(); } // System.out.println(Utils.Brackets(output)); return output; } //https://stackoverflow.com/questions/4194439/sending-commands-to-server-via-jsch-shell-channel @Override protected void CheckException(Exception ex) { if (ex instanceof WorkspaceNotFoundException) { Log.Writeln_(ex.getMessage()); return; } Throwable cause = getCauseRec(ex); if ((cause instanceof UnknownHostException) || (cause instanceof SocketException)) { Log.Writeln(machine.getFullDescription() + " не найден(а).\n" + "Проверьте наличие подключения к сети."); } else if (cause instanceof SocketTimeoutException) { Log.Writeln(machine.getFullDescription() + " не отвечает."); } else super.CheckException(ex); } public Vector read_file_lines(String path_r) throws Exception { Vector res = new Vector<>(); InputStream out = sftpChannel.get(path_r); BufferedReader br = new BufferedReader(new InputStreamReader(out)); String line; while ((line = br.readLine()) != null) res.add(line); br.close(); return res; } public void getSingleFile(String src, String dst) throws Exception { sftpChannel.get(src, dst); } //с проверкой. public boolean tryGetSingleFileWithMaxSize(RemoteFile src, File dst, int maxSize) throws Exception { if (Exists(src)) { if ((maxSize == 0) || (getFileKBSize(src.full_name) <= maxSize)) { getSingleFile(src.full_name, dst.getAbsolutePath()); return true; } else { Utils.WriteToFile(dst, "Размер файла превышает " + maxSize + " KB.\n" + "Файл не загружен. Его можно просмотреть на машине по адресу\n" + Utils.Brackets(src.full_name)); } } return false; } public void putSingleFile(File src, RemoteFile dst) throws Exception { sftpChannel.put(src.getAbsolutePath(), dst.full_name); } public void tryMKDir(RemoteFile dir) throws Exception { if (!Exists(dir)) sftpChannel.mkdir(dir.full_name); } public void tryRM(RemoteFile file) throws Exception { if (Exists(file)) sftpChannel.rm(file.full_name); } public void putSingleFile(String src, String dst) throws Exception { sftpChannel.put(src, dst); } public void put_resource(String res_name) throws Exception { putSingleFile(Utils.CreateTempResourceFile(res_name).getAbsolutePath(), res_name); } @Override public void body() throws Exception { Connect(); ServerAction(); } protected void ServerAction() throws Exception { } //https://losst.ru/komanda-find-v-linux#%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5_%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B_%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B_find public String getStarter() { return String.join("/", user.workspace, modules, starter); } public String getLauncher() { return String.join("/", user.workspace, modules, launcher); } //насчет даты. //http://i-leon.ru/tools/time //этот метод для того чтобы не пересылать подверсии. В случае тестов их наличие исключено! public void SynchronizeProjectSubDirsR(db_project_info project, File local_dir, RemoteFile remote_dir, boolean data) throws Exception { ShowMessage2("синхронизация: " + local_dir.getName()); Vector local_subdirs = project.getSubdirectoriesSimple(local_dir); Vector local_files = project.getActiveFilesForSynchronization(local_dir, data); //------------------------------------------------------------------------ LinkedHashMap remote_subdirs = new LinkedHashMap<>(); LinkedHashMap remote_files = new LinkedHashMap<>(); Vector files = sftpChannel.ls(remote_dir.full_name); for (ChannelSftp.LsEntry file : files) { if (file.getAttrs().isDir()) { if (!file.getFilename().equals(".") && !file.getFilename().equals("..")) remote_subdirs.put(file.getFilename(), new RemoteFile(remote_dir.full_name, file.getFilename(), true)); } else { RemoteFile rf = new RemoteFile(remote_dir.full_name, file.getFilename()); rf.updateTime = RemoteFile.convertUpdateTime(file.getAttrs().getMTime()); remote_files.put(file.getFilename(), rf); } } for (File lsd : local_subdirs) { RemoteFile rsd = null; if (!remote_subdirs.containsKey(lsd.getName())) sftpChannel.mkdir((rsd = new RemoteFile(remote_dir.full_name, lsd.getName(), true)).full_name); else rsd = remote_subdirs.get(lsd.getName()); SynchronizeProjectSubDirsR(project, lsd, rsd, data); } for (File lf : local_files) { RemoteFile rf = null; if (!remote_files.containsKey(lf.getName())) { rf = new RemoteFile(remote_dir.full_name, lf.getName()); ShowMessage2(lf.getName()); putSingleFile(lf, rf); } else { rf = remote_files.get(lf.getName()); if (lf.lastModified() > rf.updateTime) { ShowMessage2(lf.getName()); putSingleFile(lf, rf); } } } } protected int getFileKBSize(String path) throws Exception { return Integer.parseInt(CommandWithAnswer('\t', "du " + Utils.DQuotes(path))); } //https://stackoverflow.com/questions/4194439/sending-commands-to-server-via-jsch-shell-channel public String ShellCommand(String... commands) throws Exception { shellChannel = (ChannelShell) session.openChannel("shell"); PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(); shellChannel.setInputStream(in); shellChannel.setOutputStream(out); PipedOutputStream pin = new PipedOutputStream(in); PipedInputStream pout = new PipedInputStream(out); shellChannel.connect(); InputStreamReader fromServer = new InputStreamReader(pout); ShellParser.setUserName(user.login); ShellParser.ReadInvitation(fromServer); // System.out.println("first invitation read"); String result = ""; //формат работы с шеллом. //<эхо-команды\n><ответ команды><приглашение\n><приглашение> for (String command : commands) { // System.out.println(command); pin.write((command + "\r\n").getBytes()); ShellParser.ReadLine(fromServer); // UI.Info("echo read"); ShellParser.ReadInvitation(fromServer); // UI.Info("first invitation read"); result = ShellParser.getCommandResult(fromServer); // UI.Info("result = " + Utils.Brackets(result)); } shellChannel.disconnect(); return result; } public Vector getFilesByExtensions(RemoteFile dir, String... extensions) throws Exception { Vector res = new Vector<>(); Vector files = sftpChannel.ls(dir.full_name); for (ChannelSftp.LsEntry file : files) { String[] data = file.getFilename().split("\\."); if (data.length > 1) { String file_extension = data[data.length - 1]; for (String extension : extensions) { if (file_extension.equalsIgnoreCase(extension)) res.add(new RemoteFile(dir.full_name, file.getFilename())); } } } return res; } public void deleteFilesByExtensions(RemoteFile dir, String... extensions) throws Exception { Vector to_delete = getFilesByExtensions(dir, extensions); for (RemoteFile file : to_delete) sftpChannel.rm(file.full_name); } public void copy(RemoteFile src, RemoteFile dst) throws Exception { ShellCommand("cp " + Utils.DQuotes(src.full_name) + " " + Utils.DQuotes(dst.full_name)); } //------- public void writeToFile(String text, RemoteFile dst) throws Exception { sftpChannel.put(new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)), dst.full_name); sftpChannel.chmod(0777, dst.full_name); } public void SynchronizeSubDirsR(File local_dir, RemoteFile remote_dir) throws Exception { File[] local_subdirs = local_dir.listFiles(File::isDirectory); File[] local_files = local_dir.listFiles(File::isFile); //------------------------------------------------------------------------ LinkedHashMap remote_subdirs = new LinkedHashMap<>(); LinkedHashMap remote_files = new LinkedHashMap<>(); Vector files = sftpChannel.ls(remote_dir.full_name); for (ChannelSftp.LsEntry file : files) { if (file.getAttrs().isDir()) { if (!file.getFilename().equals(".") && !file.getFilename().equals("..")) remote_subdirs.put(file.getFilename(), new RemoteFile(remote_dir.full_name, file.getFilename(), true)); } else { RemoteFile rf = new RemoteFile(remote_dir.full_name, file.getFilename()); rf.updateTime = RemoteFile.convertUpdateTime(file.getAttrs().getMTime()); remote_files.put(file.getFilename(), rf); } } if (local_subdirs != null) { for (File lsd : local_subdirs) { if (!lsd.getName().equals(Constants.data)) { RemoteFile rsd = null; if (!remote_subdirs.containsKey(lsd.getName())) sftpChannel.mkdir((rsd = new RemoteFile(remote_dir.full_name, lsd.getName(), true)).full_name); else rsd = remote_subdirs.get(lsd.getName()); SynchronizeSubDirsR(lsd, rsd); } } } if (local_files != null) { for (File lf : local_files) { RemoteFile rf = null; if (!remote_files.containsKey(lf.getName())) { rf = new RemoteFile(remote_dir.full_name, lf.getName()); putSingleFile(lf, rf); } else { rf = remote_files.get(lf.getName()); if (lf.lastModified() > rf.updateTime) { putSingleFile(lf, rf); } } } } } public void RMDIR(String dir) throws Exception { if (!dir.isEmpty() && !dir.equals("/") && !dir.equals("\\") && !dir.equals("*")) { ShellCommand("rm -rf " + Utils.DQuotes(dir)); } else throw new PassException("Недопустимый путь для удаления папки " + Utils.DQuotes(dir)); } //скорее всего,временные методы. они есть в UserConnection, при удаленном запуске тестирования //-------------------------------------------------------------------------------- public void MKDIR(RemoteFile dir) throws Exception { if (!Exists(dir)) sftpChannel.mkdir(dir.full_name); } //-- public Pair performScript(RemoteFile directory, String... commands) throws Exception { RemoteFile script_file = new RemoteFile(directory.full_name, Constants.script); RemoteFile out = new RemoteFile(directory.full_name, Constants.out_file); RemoteFile err = new RemoteFile(directory.full_name, Constants.err_file); // Vector files = new Vector<>(); files.add(script_file); files.add(out); files.add(err); for (RemoteFile file : files) { if (Exists(directory)) sftpChannel.rm(file.full_name); } //-- writeToFile(String.join("\n", commands), script_file); //-- ShellCommand("cd " + Utils.DQuotes(directory.full_name), script_file.full_name + " 1>" + Constants.out_file + " 2>" + Constants.err_file); return new Pair<>(out, err); } //-- //-- public boolean Exists(String file_full_name) throws Exception { try { sftpChannel.stat(file_full_name); return true; } catch (SftpException e) { if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { // file doesn't exist return false; } else { // something else went wrong throw e; } } } public boolean Exists(RemoteFile file) throws Exception { return Exists(file.full_name); } }