2023-09-17 22:13:42 +03:00
package Visual_DVM_2021.Passes.SSH ;
2023-10-06 22:51:09 +03:00
import Common.Constants ;
2023-09-17 22:13:42 +03:00
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 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 < T > extends Pass_2021 < T > {
//-----------------------------------------------
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 ( sftpChannel . getHome ( ) , userWorkspace . name ) )
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 < String > read_file_lines ( String path_r ) throws Exception {
Vector < String > 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 tryGetSingleFile ( RemoteFile src , File dst , int maxSize ) throws Exception {
if ( Exists ( src . parent , src . name ) ) {
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 {
// System.out.print("try mkdir: '" + dir.full_name);
if ( ! Exists ( dir . parent , dir . name ) ) sftpChannel . mkdir ( dir . full_name ) ;
// System.out.println("..done");
}
public void tryRM ( RemoteFile file ) throws Exception {
// System.out.print("try remove: '" + file.full_name);
if ( Exists ( file . parent , file . name ) ) {
// System.out.print("' :exists.needs remove..");
sftpChannel . rm ( file . full_name ) ;
// System.out.println(" +");
} else {
// System.out.println("no such file");
}
}
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 {
}
//тут имя файла короткое.
public boolean Exists ( String folder , String name ) throws Exception {
Vector < ChannelSftp . LsEntry > files = sftpChannel . ls ( folder ) ;
for ( ChannelSftp . LsEntry file : files ) {
if ( file . getFilename ( ) . equals ( name ) ) {
return true ;
}
}
return false ;
}
//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 < File > local_subdirs = project . getSubdirectoriesSimple ( local_dir ) ;
Vector < File > local_files = project . getActiveFilesForSynchronization ( local_dir , data ) ;
//------------------------------------------------------------------------
LinkedHashMap < String , RemoteFile > remote_subdirs = new LinkedHashMap < > ( ) ;
LinkedHashMap < String , RemoteFile > remote_files = new LinkedHashMap < > ( ) ;
Vector < ChannelSftp . LsEntry > 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 ) ;
2023-10-10 16:53:29 +03:00
// UI.Info("echo read");
2023-09-17 22:13:42 +03:00
ShellParser . ReadInvitation ( fromServer ) ;
2023-10-10 16:53:29 +03:00
// UI.Info("first invitation read");
2023-09-17 22:13:42 +03:00
result = ShellParser . getCommandResult ( fromServer ) ;
2023-10-10 16:53:29 +03:00
// UI.Info("result = " + Utils.Brackets(result));
2023-09-17 22:13:42 +03:00
}
shellChannel . disconnect ( ) ;
return result ;
}
public Vector < RemoteFile > getFilesByExtensions ( RemoteFile dir , String . . . extensions ) throws Exception {
Vector < RemoteFile > res = new Vector < > ( ) ;
Vector < ChannelSftp . LsEntry > 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 < RemoteFile > 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 < String , RemoteFile > remote_subdirs = new LinkedHashMap < > ( ) ;
LinkedHashMap < String , RemoteFile > remote_files = new LinkedHashMap < > ( ) ;
Vector < ChannelSftp . LsEntry > 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 ) {
2023-10-06 22:51:09 +03:00
if ( ! lsd . getName ( ) . equals ( Constants . data ) ) {
2023-09-17 22:13:42 +03:00
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 ) ) ;
}
2023-10-10 16:53:29 +03:00
//скорее всего,временные методы. они есть в UserConnection, при удаленном запуске тестирования
//--------------------------------------------------------------------------------
public void MKDIR ( RemoteFile dir ) throws Exception {
if ( ! Exists ( dir . parent , dir . name ) ) sftpChannel . mkdir ( dir . full_name ) ;
}
//--
2023-09-17 22:13:42 +03:00
}