diff --git a/SimpleFTP/.idea/compiler.xml b/SimpleFTP/.idea/compiler.xml new file mode 100644 index 0000000..bfbbf39 --- /dev/null +++ b/SimpleFTP/.idea/compiler.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/misc.xml b/SimpleFTP/.idea/misc.xml new file mode 100644 index 0000000..bc8d0a3 --- /dev/null +++ b/SimpleFTP/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules.xml b/SimpleFTP/.idea/modules.xml new file mode 100644 index 0000000..8bef07e --- /dev/null +++ b/SimpleFTP/.idea/modules.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/ClientGUI/ClientGUI.iml b/SimpleFTP/.idea/modules/ClientGUI/ClientGUI.iml new file mode 100644 index 0000000..727babf --- /dev/null +++ b/SimpleFTP/.idea/modules/ClientGUI/ClientGUI.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/FTPClient/FTPClient.iml b/SimpleFTP/.idea/modules/FTPClient/FTPClient.iml new file mode 100644 index 0000000..608393c --- /dev/null +++ b/SimpleFTP/.idea/modules/FTPClient/FTPClient.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/FTPClient/FTPClient_main.iml b/SimpleFTP/.idea/modules/FTPClient/FTPClient_main.iml new file mode 100644 index 0000000..957e157 --- /dev/null +++ b/SimpleFTP/.idea/modules/FTPClient/FTPClient_main.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/FTPClient/FTPClient_test.iml b/SimpleFTP/.idea/modules/FTPClient/FTPClient_test.iml new file mode 100644 index 0000000..f1fb24b --- /dev/null +++ b/SimpleFTP/.idea/modules/FTPClient/FTPClient_test.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/FTPServer/FTPServer.iml b/SimpleFTP/.idea/modules/FTPServer/FTPServer.iml new file mode 100644 index 0000000..a882b10 --- /dev/null +++ b/SimpleFTP/.idea/modules/FTPServer/FTPServer.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/FTPServer/FTPServer_main.iml b/SimpleFTP/.idea/modules/FTPServer/FTPServer_main.iml new file mode 100644 index 0000000..e96af3e --- /dev/null +++ b/SimpleFTP/.idea/modules/FTPServer/FTPServer_main.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/FTPServer/FTPServer_test.iml b/SimpleFTP/.idea/modules/FTPServer/FTPServer_test.iml new file mode 100644 index 0000000..e408f50 --- /dev/null +++ b/SimpleFTP/.idea/modules/FTPServer/FTPServer_test.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/SimpleFTP.iml b/SimpleFTP/.idea/modules/SimpleFTP.iml new file mode 100644 index 0000000..6dc9039 --- /dev/null +++ b/SimpleFTP/.idea/modules/SimpleFTP.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/SimpleFTP_main.iml b/SimpleFTP/.idea/modules/SimpleFTP_main.iml new file mode 100644 index 0000000..b57c78d --- /dev/null +++ b/SimpleFTP/.idea/modules/SimpleFTP_main.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/modules/SimpleFTP_test.iml b/SimpleFTP/.idea/modules/SimpleFTP_test.iml new file mode 100644 index 0000000..fa66cbb --- /dev/null +++ b/SimpleFTP/.idea/modules/SimpleFTP_test.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/.idea/vcs.xml b/SimpleFTP/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/SimpleFTP/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SimpleFTP/FTPClient/build.gradle b/SimpleFTP/FTPClient/build.gradle new file mode 100644 index 0000000..c73fa91 --- /dev/null +++ b/SimpleFTP/FTPClient/build.gradle @@ -0,0 +1,14 @@ +group 'ru.spbau.dkaznacheev' +version '1.0-SNAPSHOT' + +apply plugin: 'java' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' +} diff --git a/SimpleFTP/FTPClient/src/main/java/ru/spbau/dkaznacheev/simpleftp/Client.java b/SimpleFTP/FTPClient/src/main/java/ru/spbau/dkaznacheev/simpleftp/Client.java new file mode 100644 index 0000000..77cb2ed --- /dev/null +++ b/SimpleFTP/FTPClient/src/main/java/ru/spbau/dkaznacheev/simpleftp/Client.java @@ -0,0 +1,89 @@ +package ru.spbau.dkaznacheev.simpleftp; + +import java.io.*; +import java.net.ConnectException; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.Scanner; + +public class Client { + + /** + * Downloads a file from sever + * @param in inputstream of a socket + * @param name name of the file + */ + private static void receiveFile(DataInputStream in, String name) throws IOException{ + long length = in.readLong(); + if (length == 0) { + System.out.println("No such file"); + return; + } + try (FileOutputStream out = new FileOutputStream(simpleName(name))) { + byte[] buffer = new byte[4096]; + long remaining = length; + int read; + while ((read = in.read(buffer, 0, (int) Math.min(buffer.length, remaining))) > 0) { + remaining -= read; + out.write(buffer); + } + } + } + + /** + * Returns simple file name of the filepath. + * @param path path to file + * @return filename + */ + private static String simpleName(String path) { + return new File(path).getName(); + } + + public static void main(String[] args) { + try ( + Socket socket = new Socket("127.0.0.1", 8080); + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + DataInputStream in = new DataInputStream(socket.getInputStream()); + Scanner stdIn = new Scanner(System.in) + ) { + ResponseCode fromServer; + String fromUser; + boolean exit = false; + while (!exit) { + fromUser = stdIn.nextLine(); + if (fromUser != null) { + out.println(fromUser); + } + fromServer = ResponseCode.values()[in.readInt()]; + switch (fromServer) { + case CLOSE_CONNECTION: { + exit = true; + break; + } + case INVALID_COMMAND: { + System.out.println("Invalid command"); + break; + } + case FOLDER_DESCRIPTION: { + FolderDescription description = FolderDescription.read(in); + description.print(); + break; + } + case FILE_SEND: { + String[] parts = fromUser.split(" "); + receiveFile(in, parts[1]); + break; + } + } + } + + } catch (UnknownHostException e) { + System.err.println("Unknown host"); + } catch (ConnectException e) { + System.err.println("Connection refused"); + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/SimpleFTP/FTPServer/build.gradle b/SimpleFTP/FTPServer/build.gradle new file mode 100644 index 0000000..c73fa91 --- /dev/null +++ b/SimpleFTP/FTPServer/build.gradle @@ -0,0 +1,14 @@ +group 'ru.spbau.dkaznacheev' +version '1.0-SNAPSHOT' + +apply plugin: 'java' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' +} diff --git a/SimpleFTP/FTPServer/src/main/java/ru/spbau/dkaznacheev/simpleftp/Server.java b/SimpleFTP/FTPServer/src/main/java/ru/spbau/dkaznacheev/simpleftp/Server.java new file mode 100644 index 0000000..d184aa8 --- /dev/null +++ b/SimpleFTP/FTPServer/src/main/java/ru/spbau/dkaznacheev/simpleftp/Server.java @@ -0,0 +1,111 @@ +package ru.spbau.dkaznacheev.simpleftp; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; + +import static ru.spbau.dkaznacheev.simpleftp.ResponseCode.*; + +/** + * Simple FTP server that can handle multiple clients and send files over sockets. + */ +public class Server { + + /** + * Sends file over ServerSocket's DataOutputStream. + * @param name filename + * @param out output stream + */ + private static void sendFile(String name, DataOutputStream out) throws IOException{ + File file = new File(name); + if (!file.exists()) { + out.writeLong(0); + return; + } + out.writeLong(file.length()); + byte[] buffer = new byte[4096]; + int read; + try (FileInputStream in = new FileInputStream(file)) { + while ((read = in.read(buffer)) > -1) { + out.write(buffer, 0, read); + } + } + + } + + /** + * Client handler, runs in a separate thread as long as there is a connectin with a client. + */ + private static class FTPThread extends Thread { + + /** + * Socket f server-client connection + */ + private Socket socket; + + public FTPThread(Socket socket) { + this.socket = socket; + } + + @Override + public void run() { + try ( + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + BufferedReader in = new BufferedReader( + new InputStreamReader(socket.getInputStream())) + ) { + String inputLine; + while ((inputLine = in.readLine()) != null) { + if (inputLine.equals("0")) { + out.writeInt(CLOSE_CONNECTION.ordinal()); + break; + } + + String[] parts = inputLine.split(" "); + if (parts.length != 2) { + out.writeInt(INVALID_COMMAND.ordinal()); + continue; + } + String command = parts[0]; + String path = parts[1]; + + if (command.equals("1")) { + FolderDescription description = FolderDescription.describeFolder(path); + out.writeInt(FOLDER_DESCRIPTION.ordinal()); + description.write(out); + } else if (command.equals("2")) { + out.writeInt(FILE_SEND.ordinal()); + sendFile(path, out); + } else { + out.writeInt(INVALID_COMMAND.ordinal()); + } + } + socket.close(); + } catch (SocketException e) { + System.out.println("Goodbye"); + } + catch (IOException e) { + System.err.println("IOException on thread " + Thread.currentThread().getName()); + } + } + } + + public static void main(String[] args) { + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(8080); + } catch (IOException e) { + System.err.println("Error creating server"); + return; + } + while (true) { + try { + Socket clientSocket = serverSocket.accept(); + new FTPThread(clientSocket).start(); + } catch (IOException e) { + System.err.println("Exception on client connecting"); + } + } + } +} diff --git a/SimpleFTP/GUIClient/GUIClient.iml b/SimpleFTP/GUIClient/GUIClient.iml new file mode 100644 index 0000000..999ca21 --- /dev/null +++ b/SimpleFTP/GUIClient/GUIClient.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleFTP/GUIClient/src/ru/spbau/dkaznacheev/simpleftp/gui/Controller.java b/SimpleFTP/GUIClient/src/ru/spbau/dkaznacheev/simpleftp/gui/Controller.java new file mode 100644 index 0000000..10a9dc6 --- /dev/null +++ b/SimpleFTP/GUIClient/src/ru/spbau/dkaznacheev/simpleftp/gui/Controller.java @@ -0,0 +1,178 @@ +package ru.spbau.dkaznacheev.simpleftp.gui; + +import javafx.fxml.FXML; +import javafx.geometry.Pos; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +import javafx.scene.layout.VBox; +import ru.spbau.dkaznacheev.simpleftp.FolderDescription; +import ru.spbau.dkaznacheev.simpleftp.FolderDescription.FileDescription; +import ru.spbau.dkaznacheev.simpleftp.ResponseCode; + +import java.io.DataInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.StringJoiner; + +import static ru.spbau.dkaznacheev.simpleftp.ResponseCode.FILE_SEND; +import static ru.spbau.dkaznacheev.simpleftp.ResponseCode.FOLDER_DESCRIPTION; + +public class Controller { + private String currentPath = "."; + + private PrintWriter out; + private DataInputStream in; + + @FXML + VBox buttonContainer; + + /** + * Downloads a file from sever + * @param in inputstream of a socket + * @param name name of the file + */ + private static void receiveFile(DataInputStream in, String name) throws IOException{ + long length = in.readLong(); + if (length == 0) { + System.out.println("No such file"); + return; + } + try (FileOutputStream out = new FileOutputStream(name)) { + byte[] buffer = new byte[4096]; + long remaining = length; + int read; + while ((read = in.read(buffer, 0, (int) Math.min(buffer.length, remaining))) > 0) { + remaining -= read; + out.write(buffer); + } + } + } + + private boolean initClient() { + try { + Socket socket = new Socket("127.0.0.1", 8080); + out = new PrintWriter(socket.getOutputStream(), true); + in = new DataInputStream(socket.getInputStream()); + } catch (IOException e) { + return false; + } + return true; + } + + private static String getNewPath(String path, String name) { + if (path.equals("./")) { + return name; + } else { + return path + "/" + name; + } + } + + private FolderDescription requestFolderDescription(String name) { + System.out.println("1 " + name); + out.println("1 " + name); + try { + ResponseCode fromServer = ResponseCode.values()[in.readInt()]; + if (fromServer != FOLDER_DESCRIPTION) { + return null; + } + return FolderDescription.read(in); + } catch (IOException e) { + return null; + } + } + + private boolean getAndShowFolder(String name) { + FolderDescription desc = requestFolderDescription(name); + if (desc == null) { + waitAlert(AlertType.ERROR, "Wrong folder", "OK to close"); + return false; + } + if (desc.getSize() == 0) { + waitAlert(AlertType.INFORMATION, "Empty folder", "OK to close"); + return false; + } + refreshFolder(desc.getFiles()); + return true; + } + + private void getAndDownloadFile(String name) { + out.println("2 " + getNewPath(currentPath, name)); + try { + ResponseCode fromServer = ResponseCode.values()[in.readInt()]; + if (fromServer != FILE_SEND) { + throw new IOException(); + } + receiveFile(in, name); + waitAlert(AlertType.INFORMATION, "File downloaded", name + " downloaded!"); + } catch (IOException e) { + waitAlert(AlertType.ERROR, "Could not download file", "OK to close"); + } + } + + private void refreshFolder(FileDescription[] files) { + buttonContainer.getChildren().clear(); + Button backButton = new Button(); + backButton.setAlignment(Pos.BASELINE_LEFT); + backButton.setPrefWidth(Double.MAX_VALUE); + backButton.setText(".."); + backButton.setStyle("-fx-background-color: #a5ffcf"); + backButton.setOnAction((e) -> { + getAndShowFolder(backFolder()); + currentPath = backFolder(); + }); + buttonContainer.getChildren().add(backButton); + for (FileDescription file : files) { + Button button = new Button(); + button.setAlignment(Pos.BASELINE_LEFT); + button.setPrefWidth(Double.MAX_VALUE); + button.setText(file.name); + if (file.isDir) { + button.setStyle("-fx-background-color: #a5ffcf"); + button.setOnAction((e) -> { + if (getAndShowFolder(getNewPath(currentPath, file.name))) { + currentPath = getNewPath(currentPath, file.name); + } + }); + } else { + button.setStyle("-fx-background-color: #e2edff"); + button.setOnAction((e) -> getAndDownloadFile(file.name)); + } + buttonContainer.getChildren().add(button); + } + } + + private String backFolder() { + if (currentPath.equals("./")) { + return currentPath; + } + String[] parts = currentPath.split("/"); + if (parts.length == 2) { + return "./"; + } + StringJoiner joiner = new StringJoiner("/"); + for (int i = 0; i < parts.length - 1; i++) { + joiner.add(parts[i]); + } + return joiner.toString(); + } + + private void waitAlert(Alert.AlertType type, String title, String message) { + Alert alert = new Alert(type); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } + + @FXML + public void initialize() { + while (!initClient()) { + waitAlert(AlertType.ERROR,"Connection error", "OK to retry"); + } + + getAndShowFolder(currentPath); + } +} diff --git a/SimpleFTP/GUIClient/src/ru/spbau/dkaznacheev/simpleftp/gui/Main.java b/SimpleFTP/GUIClient/src/ru/spbau/dkaznacheev/simpleftp/gui/Main.java new file mode 100644 index 0000000..04c1e3f --- /dev/null +++ b/SimpleFTP/GUIClient/src/ru/spbau/dkaznacheev/simpleftp/gui/Main.java @@ -0,0 +1,23 @@ +package ru.spbau.dkaznacheev.simpleftp.gui; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class Main extends Application { + + @Override + public void start(Stage primaryStage) throws Exception{ + Parent root = FXMLLoader.load(getClass().getResource("sample.fxml")); + primaryStage.setTitle("Hello World"); + primaryStage.setScene(new Scene(root, 400, 500)); + primaryStage.show(); + } + + + public static void main(String[] args) { + launch(args); + } +} diff --git a/SimpleFTP/GUIClient/src/ru/spbau/dkaznacheev/simpleftp/gui/sample.fxml b/SimpleFTP/GUIClient/src/ru/spbau/dkaznacheev/simpleftp/gui/sample.fxml new file mode 100644 index 0000000..0243472 --- /dev/null +++ b/SimpleFTP/GUIClient/src/ru/spbau/dkaznacheev/simpleftp/gui/sample.fxml @@ -0,0 +1,15 @@ + + + + + + + + + + +