diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml
index 46a2f6a..db94fa1 100644
--- a/.github/workflows/dev.yml
+++ b/.github/workflows/dev.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Build
run: cargo build --release --verbose
- name: Run tests
diff --git a/.github/workflows/pr_main.yml b/.github/workflows/pr_main.yml
index db4e60d..8ac36d2 100644
--- a/.github/workflows/pr_main.yml
+++ b/.github/workflows/pr_main.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Build
run: cargo build --release --verbose
- name: Run tests
@@ -25,7 +25,7 @@ jobs:
runs-on: windows-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Build
run: cargo build --release --verbose
- name: Run tests
@@ -37,7 +37,7 @@ jobs:
runs-on: macos-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Build
run: cargo build --release --verbose
- name: Run tests
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 58c0fab..e2611ed 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -2,99 +2,95 @@ name: Rust -> Build & Test & Release
on:
push:
- branches: [ "main" ]
+ branches: ["main"]
env:
CARGO_TERM_COLOR: always
+ PROJECT_NAME: ${{ github.event.repository.name }}
jobs:
build_linux:
-
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - name: Build
- run: cargo build --release --verbose
- - name: Run tests
- run: cargo test --verbose
+ - uses: actions/checkout@v4
+ - name: Build
+ run: cargo build --release --verbose
+ - name: Run tests
+ run: cargo test --verbose
- - name: Upload Linux Binary
- uses: actions/upload-artifact@v3
- with:
- name: rust_tcp_file_transfer_linux_x64_86
- path: target/release/*transfer
+ - name: Upload Linux Binary
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.PROJECT_NAME }}-linux-x64_86
+ path: target/release/${{ env.PROJECT_NAME }}
build_windows:
-
runs-on: windows-latest
steps:
- - uses: actions/checkout@v3
- - name: Build
- run: cargo build --release --verbose
- - name: Run tests
- run: cargo test --verbose
+ - uses: actions/checkout@v4
+ - name: Build
+ run: cargo build --release --verbose
+ - name: Run tests
+ run: cargo test --verbose
- - name: Upload Windows Binary
- uses: actions/upload-artifact@v3
- with:
- name: rust_tcp_file_transfer_windows_x64_86
- path: target/release/*transfer.exe
+ - name: Upload Windows Binary
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.PROJECT_NAME }}-windows-x64_86
+ path: target/release/${{ env.PROJECT_NAME }}.exe
build_macos:
-
runs-on: macos-latest
steps:
- - uses: actions/checkout@v3
- - name: Build
- run: cargo build --release --verbose
- - name: Run tests
- run: cargo test --verbose
+ - uses: actions/checkout@v4
+ - name: Build
+ run: cargo build --release --verbose
+ - name: Run tests
+ run: cargo test --verbose
- - name: Upload MacOS Binary
- uses: actions/upload-artifact@v3
- with:
- name: rust_tcp_file_transfer_macos_x64_86
- path: target/release/*transfer
+ - name: Upload MacOS Binary
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.PROJECT_NAME }}-macos-arm64
+ path: target/release/${{ env.PROJECT_NAME }}
release:
needs: [build_linux, build_windows, build_macos]
runs-on: ubuntu-latest
- permissions:
- contents: write
+ permissions:
+ contents: write
steps:
+ - uses: actions/checkout@v4
+ - name: Forge a Folder
+ run: mkdir Downloads
+ working-directory: /home/runner/work/${{ env.PROJECT_NAME }}/${{ env.PROJECT_NAME }}/
- - uses: actions/checkout@v3
- - name: Forge a Folder
- run: mkdir Downloads
- working-directory: /home/runner/work/rust-tcp-file-transfer/rust-tcp-file-transfer/
+ - uses: actions/download-artifact@v4
+ name: Download
+ with:
+ path: Downloads/
- - uses: actions/download-artifact@v3
- name: Download
- with:
- path: Downloads/
-
- - name: Rename Binaries
- run: |
- mv Downloads/rust_tcp_file_transfer_linux_x64_86/rust-tcp-file-transfer Downloads/rust_tcp_file_transfer_linux_x64_86/rust-tcp-file-transfer-linux_x64_86
- mv Downloads/rust_tcp_file_transfer_windows_x64_86/rust-tcp-file-transfer.exe Downloads/rust_tcp_file_transfer_windows_x64_86/rust-tcp-file-transfer-windows_x64_86.exe
- mv Downloads/rust_tcp_file_transfer_macos_x64_86/rust-tcp-file-transfer Downloads/rust_tcp_file_transfer_macos_x64_86/rust-tcp-file-transfer-macos_x64_86
+ - name: Rename Binaries
+ run: |
+ tree Downloads/
+ mv Downloads/${{ env.PROJECT_NAME }}-linux-x64_86/${{ env.PROJECT_NAME }} Downloads/${{ env.PROJECT_NAME }}-linux-x64_86/${{ env.PROJECT_NAME }}-linux-x64_86
+ mv Downloads/${{ env.PROJECT_NAME }}-windows-x64_86/${{ env.PROJECT_NAME }}.exe Downloads/${{ env.PROJECT_NAME }}-windows-x64_86/${{ env.PROJECT_NAME }}-windows-x64_86.exe
+ mv Downloads/${{ env.PROJECT_NAME }}-macos-arm64/${{ env.PROJECT_NAME }} Downloads/${{ env.PROJECT_NAME }}-macos-arm64/${{ env.PROJECT_NAME }}-macos-arm64
+ - name: Git Commit SHA
+ id: vars
+ run: |
+ calculatedSha=$(git rev-parse --short ${{ github.sha }})
+ echo "short_sha=$calculatedSha" >> $GITHUB_OUTPUT
- - name: Git Commit SHA
- id: vars
- run: |
- calculatedSha=$(git rev-parse --short ${{ github.sha }})
- echo "short_sha=$calculatedSha" >> $GITHUB_OUTPUT
-
- - uses: softprops/action-gh-release@v0.1.15
- name: Release
- with:
- tag_name: ${{ steps.vars.outputs.short_sha }}
- generate_release_notes: true
- files: |
- Downloads/*linux*/*transfer*
- Downloads/*windows*/*transfer*
- Downloads/*macos*/*transfer*
-
\ No newline at end of file
+ - uses: softprops/action-gh-release@v2
+ name: Release
+ with:
+ tag_name: ${{ steps.vars.outputs.short_sha }}
+ generate_release_notes: true
+ files: |
+ Downloads/*linux*/${{ env.PROJECT_NAME }}*
+ Downloads/*windows*/${{ env.PROJECT_NAME }}*
+ Downloads/*macos*/${{ env.PROJECT_NAME }}*
diff --git a/.gitignore b/.gitignore
index 196e176..32d71ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
# will have compiled files and executables
debug/
target/
+.vscode/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
diff --git a/Cargo.toml b/Cargo.toml
index bec9c30..f4a5455 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,10 @@ strip = "symbols"
opt-level = 3
overflow-checks = true
lto = true
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+codegen-units = 1
+panic = "abort"
+
+[lints.rust]
+unsafe_code = "forbid"
[dependencies]
diff --git a/README.md b/README.md
index 8bf8d1d..b80f7bd 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,25 @@
# rust-tcp-file-transfer
TCP File Transfer Server and Client in Rust
+**Usage**
+> ./rust-tcp-file-transfer -h
+>
+
+
+
+**Examples**
+> ./rust-tcp-file-transfer -sv -s -l ~/Desktop/cat.png
+>
+> ./rust-tcp-file-transfer -cl -r -l ~/Documents/
+
+
+
+**TO-DO List**
+
+☑ Standard library only.
+
☑ File transfer over network.
☑ Remove memory limitations. [d42412c](https://github.com/Tahinli/rust-tcp-file-transfer/pull/1/commits/d42412c57d7d95672ba64b3e489b95f1c4b04a08)
☑ Bidirectional transfer. [b0531de](https://github.com/Tahinli/rust-tcp-file-transfer/commit/b0531deb257332f46fc76de16d3a17fb3b28acee)
-
-☐ Folder transfer.
-
-☐ Remember where it stopped.
-
-☐ Reach over NAT (peer to peer).
-
-☐ Async.
diff --git a/assets/example.png b/assets/example.png
new file mode 100644
index 0000000..c7f3952
Binary files /dev/null and b/assets/example.png differ
diff --git a/assets/help_menu.png b/assets/help_menu.png
new file mode 100644
index 0000000..311613f
Binary files /dev/null and b/assets/help_menu.png differ
diff --git a/src/main.rs b/src/main.rs
index 49aeb6f..41d3a98 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,503 +1,729 @@
-use std::fs::{File, Metadata, self};
-use std::time::Instant;
-use std::net::{TcpListener, TcpStream};
-use std::io::{Read, Write, self, BufWriter, BufReader, BufRead};
use std::env::{self};
+use std::fs::{self, File, Metadata};
+use std::io::{BufWriter, Read, Write};
+use std::net::{IpAddr, TcpListener, TcpStream};
+use std::path::{Path, PathBuf};
+use std::time::Instant;
+const BUFFER_SIZE: u64 = 100000;
-const BUFFER_SIZE:u64 = 100000;
-struct FileInfo
- {
- file:Option,
- location:String,
- size_current:usize,
- metadata:Option,
- }
-impl FileInfo
- {
- fn reading_operations(&mut self, stream:&mut TcpStream, debug_mode:&bool)
- {
- self.read_metadata();
- match self.metadata
- {
- Some(ref mut metadata) =>
- {
- if Metadata::is_file(metadata)
- {
- self.open_file();
- self.send_file(stream, debug_mode);
- }
- else if Metadata::is_symlink(metadata)
- {
- self.open_file();
- self.send_file(stream, debug_mode);
- }
- else
- {
- //path recognition and creation on the other side
- //std:path
- panic!("\n\tError: Folder Transfers've not Supported yet\n")
- }
- }
- None =>
- {
- println!("Error: Read Metadata -> {}", self.location);
- }
- }
- }
- fn writing_operations(&mut self, stream:&mut TcpStream, debug_mode:&bool)
- {
- self.forge_file();
- self.write_file(stream, debug_mode);
- }
- fn read_metadata(&mut self)
- {
- self.metadata = Some(fs::metadata(&self.location).expect("Error: Read Metadata"));
- }
- fn open_file(&mut self)
- {
- match File::open(&self.location)
- {
- Ok(file) =>
- {
- self.file = Some(file);
- }
- Err(err_val) =>
- {
- println!("Error: Open File -> {} | Error: {}", self.location, err_val);
- return;
- }
- }
-
- }
- fn send_file(&mut self, stream:&mut TcpStream, debug_mode:&bool)
- {
- let size = self.metadata.as_ref().unwrap().len();
- let mut iteration = (size/BUFFER_SIZE)+1;
- let total_iteration = iteration;
- self.handshake_validation(stream, size, debug_mode);
- println!("Size = {}", size);
- println!("Iteration = {}", iteration);
- while iteration != 0
- {
- iteration -= 1;
- let mut buffer = [0u8;BUFFER_SIZE as usize];
- if iteration != 0
- {
- self.read_exact(&mut buffer, debug_mode);
- }
- else
- {
- self.read_exact(&mut buffer[..(size%BUFFER_SIZE) as usize], debug_mode);
- }
- if *debug_mode
- {
- println!("Read Data = {:#?}", buffer);
- }
- self.send_exact(&mut buffer, stream, debug_mode);
- println!("%{}", 100 as f64 -((iteration as f64/total_iteration as f64)*100 as f64));
- }
- }
- fn handshake_validation(&mut self, stream:&mut TcpStream, size:u64, debug_mode:&bool)
- {
- self.send_exact(String::from(size.to_string()+"\n").as_bytes(), stream, debug_mode);
- match self.recv_until(stream, '\n', debug_mode)
- {
- Some(handshake_callback) =>
- {
- if handshake_callback == size.to_string().as_bytes().to_vec()
- {
- println!("Done: Handshake -> {}", self.location);
- if *debug_mode
- {
- println!("{:#?} ", handshake_callback);
- }
- }
- else
- {
- println!("Error: Handshake -> {}", self.location);
- println!("{:#?} ", handshake_callback);
- panic!()
- }
- }
- None =>
- {
- panic!()
- }
- }
- }
- fn read_exact(&mut self, buffer:&mut [u8], debug_mode:&bool)
- {
- match self.file.as_ref().unwrap().read_exact(buffer)
- {
- Ok(_) =>
- {
- if *debug_mode
- {
- println!("Done: Read Bytes -> {}", self.location);
- println!("{:#?}", buffer);
- }
- }
- Err(err_val) =>
- {
- println!("Error: Read Bytes -> {} | Error: {}", self.location, err_val);
- panic!()
- }
- }
- }
- fn send_exact(&mut self, buffer:&[u8], stream:&mut TcpStream, debug_mode:&bool)
- {
- let mut stream_writer = BufWriter::new(stream.try_clone().unwrap());
- match stream_writer.write_all(buffer)
- {
- Ok(_) =>
- {
- self.size_current += buffer.len();
- if *debug_mode
- {
- println!("Done: Send Bytes -> {}", self.location);
- println!("{:#?}", buffer);
- }
- }
- Err(err_val) =>
- {
- println!("Error: Send Bytes -> {} | Error: {}", self.location, err_val);
- panic!();
- }
- }
- match stream_writer.flush()
- {
- Ok(_) =>
- {
- if *debug_mode
- {
- println!("Done: Flush -> {}", self.location);
- }
- }
- Err(err_val) =>
- {
- println!("Error: Flush -> {} | Error: {}", self.location, err_val);
- panic!()
- }
- }
- }
- fn recv_exact(&mut self, buffer:&mut [u8], stream:&mut TcpStream, debug_mode:&bool)
- {
- match stream.read_exact(buffer)
- {
- Ok(_) =>
- {
- self.size_current += buffer.len();
- if *debug_mode
- {
- println!("Done: Receive Bytes -> {}", self.location);
- println!("{:#?}", buffer);
- }
- }
- Err(err_val) =>
- {
- println!("Error: Receive Bytes -> {} | Error: {}", self.location, err_val);
- panic!();
- }
- }
- }
- fn recv_until(&mut self, stream:&mut TcpStream, until:char, debug_mode:&bool) -> Option>
- {
- let mut buffer = Vec::new();
- let mut stream_reader = BufReader::new(stream.try_clone().unwrap());
- match stream_reader.read_until(until as u8,&mut buffer)
- {
- Ok(_) =>
- {
- if *debug_mode
- {
- println!("Done: Receive Until -> {}", self.location);
- println!("{:#?}", buffer);
- }
- buffer.pop();
- }
- Err(err_val) =>
- {
- println!("Error: Receive Until -> {} | Error: {}", self.location, err_val);
- return None;
- }
- }
- return Some(buffer);
- }
- fn forge_file(&mut self)
- {
- self.file = Some(File::create(&self.location).expect("Error: Create File"));
- }
- fn handshake_recv(&mut self, stream:&mut TcpStream, debug_mode:&bool) -> u64
- {
- match self.recv_until(stream, '\n', debug_mode)
- {
- Some(mut handshake) =>
- {
- println!("Done: Handshake -> {}", self.location);
- if *debug_mode
- {
- println!("{:#?} ", handshake);
- }
- let size = String::from_utf8(handshake.clone()).unwrap().parse().unwrap();
- handshake.push(b'\n');
- self.send_exact(&handshake.as_slice(), stream, debug_mode);
- size
- }
- None =>
- {
- println!("Error: Handshake -> {}", self.location);
- 0
- }
- }
- }
- fn save_exact(&mut self, buffer:&[u8], debug_mode:&bool)
- {
- let mut file_writer = BufWriter::new(self.file.as_ref().unwrap());
- match file_writer.write_all(buffer)
- {
- Ok(_) =>
- {
- if *debug_mode
- {
- println!("Done: Write -> {} | {} bytes", self.location, self.size_current);
- println!("{:#?}", buffer);
- }
- }
- Err(err_val) =>
- {
- println!("Error: Write -> {} | Error: {}", self.location,err_val);
- panic!();
- }
- }
- match file_writer.flush()
- {
- Ok(_) =>
- {
- if *debug_mode
- {
- println!("Done: Flush -> {}", self.location);
- }
- }
- Err(err_val) =>
- {
- println!("Error: Flush -> {} | Error: {}", self.location,err_val);
- panic!();
- }
- }
- }
- fn write_file(&mut self, stream:&mut TcpStream, debug_mode:&bool)
- {
- let size = self.handshake_recv(stream, debug_mode);
- let mut iteration:u64 = (size/BUFFER_SIZE)+1;
- let total_iteration = iteration;
- println!("Size = {}", size);
- println!("Iteration = {}", iteration);
- while iteration != 0
- {
- iteration -= 1;
- let mut buffer = [0u8;BUFFER_SIZE as usize];
- self.recv_exact(&mut buffer, stream, debug_mode);
- if iteration != 0
- {
- self.save_exact(&buffer, debug_mode);
- }
- else
- {
- self.save_exact(&buffer[..(size%BUFFER_SIZE) as usize], debug_mode);
- }
- println!("%{}", 100 as f64 -((iteration as f64/total_iteration as f64)*100 as f64));
- }
- }
- }
-enum DebugMode
- {
- On,
- Off
- }
-impl DebugMode {
- fn debug_mode(self) -> bool
- {
- match self
- {
- DebugMode::On =>
- {
- println!("Debug: ON");
- let debug = true;
- debug
- }
- DebugMode::Off =>
- {
- println!("Debug: OFF");
- let debug = false;
- debug
- }
- }
- }
+#[derive(Debug)]
+enum FileType {
+ Symlink,
+ File,
+ Folder,
}
-enum Connection
- {
- Server(String, String),
- Client(String, String),
- }
-
-impl Connection
- {
- fn server(self, file_info:&mut FileInfo, debug_mode:bool)
- {
- print!("Server -> ");
- let ip:String;
- let port:String;
- let address:String;
- match self
- {
- Connection::Server(in1, in2) =>
- {
- ip = in1.trim_end().to_string();
- port = in2.trim_end().to_string();
- address = format!("{}:{}", ip, port);
- println!("{}", address);
- }
- _ => return
- }
- let socket = TcpListener::bind(address);
- for stream in socket.expect("Error: Can't Check Connections").incoming()
- {
- match stream
- {
- Ok(mut stream) =>
- {
- println!("Connected");
- send_or_receive(file_info, &mut stream, &debug_mode);
- }
- Err(e) =>
- {
- println!("Error: Can't Visit Stream -> {}", e);
- return;
- }
- }
- }
- }
- fn client(self, file_info:&mut FileInfo, debug_mode:bool)
- {
- print!("Client -> ");
- let ip:String;
- let port:String;
- let address:String;
- match self
- {
- Connection::Client(in1, in2) =>
- {
- ip = in1.trim_end().to_string();
- port = in2.trim_end().to_string();
- address = format!("{}:{}", ip, port);
- println!("{}", address);
- }
- _ => return
- }
- match TcpStream::connect(address)
- {
- Ok(mut stream) =>
- {
- println!("Connected");
- send_or_receive(file_info, &mut stream, &debug_mode);
- }
- Err(e) =>
- {
- println!("Error: Connection -> {}", e);
- }
- }
-
- }
- }
-fn send_or_receive(file_info:&mut FileInfo, stream:&mut TcpStream, debug_mode:&bool)
- {
- match &take_string("Input: Send 's', Receive 'r'".to_string())[..1]
- {
- "s" =>
- {
- println!("Connected");
- let start_time = Instant::now();
- FileInfo::reading_operations(file_info, stream, &debug_mode);
- let finish_time = Instant::now();
- println!("Passed: Total -> {:#?}", finish_time.duration_since(start_time));
+impl FileType {
+ fn what_type(value: u8, file_info: &FileInfo, debug_mode: &bool) -> FileType {
+ match value {
+ 100 => {
+ if *debug_mode {
+ println!(
+ "Done: Symlink Detected -> {}",
+ file_info.location.as_ref().unwrap()
+ );
}
- "r" =>
- {
- let start_time = Instant::now();
- FileInfo::writing_operations(file_info, stream, &debug_mode);
- let finish_time = Instant::now();
- println!("Passed: Total -> {:#?}", finish_time.duration_since(start_time));
+ FileType::Symlink
+ }
+ 101 => {
+ if *debug_mode {
+ println!(
+ "Done: File Detected -> {}",
+ file_info.location.as_ref().unwrap()
+ );
}
- input =>
- {
- println!("Error: Give Valid Input, You Gave : {}", input);
- panic!() }
+ FileType::File
+ }
+ 102 => {
+ if *debug_mode {
+ println!(
+ "Done: Folder Detected -> {}",
+ file_info.location.as_ref().unwrap()
+ );
+ }
+ FileType::Folder
+ }
+ _ => {
+ println!(
+ "Error: Undefined Type -> {}",
+ file_info.location.as_ref().unwrap()
+ );
+ panic!()
+ }
}
}
-fn take_string(output:String) -> String
- {
- let mut input = String::new();
- println!("{}", output);
- io::stdin().read_line(&mut input).expect("Error: Failed to Read from Console");
- input
+}
+#[derive(Debug)]
+struct UserEnvironment {
+ ip: IpAddr,
+ port: u16,
+ server: bool,
+ send: bool,
+ location: Option,
+ debug: bool,
+}
+impl UserEnvironment {
+ fn new() -> UserEnvironment {
+ UserEnvironment {
+ ip: "127.0.0.1".parse().unwrap(),
+ port: 2121,
+ server: false,
+ send: false,
+ location: None,
+ debug: false,
+ }
}
-fn take_arg() -> String
- {
- env::args().last().as_deref().unwrap_or("default").to_string()
+}
+#[derive(Debug)]
+struct FileInfo {
+ file: Option,
+ location: Option,
+ sign: Option,
+ size_total: u64,
+ size_current: u64,
+ metadata: Option,
+ progress: u8,
+}
+impl FileInfo {
+ fn new() -> FileInfo {
+ FileInfo {
+ file: None,
+ location: None,
+ sign: None,
+ size_total: 0,
+ size_current: 0,
+ metadata: None,
+ progress: 0,
+ }
}
-fn debug_mod() -> DebugMode
- {
- match &take_string("Input: Debug -> On '1', Debug -> Off '0'".to_string())[0..1]
- {
- "1" =>
- {
- DebugMode::On
+ fn pass_user_environment(&mut self, user_environment: &UserEnvironment) {
+ self.location = user_environment.location.clone();
+ self.sign = user_environment.location.clone();
+ }
+ fn reading_operations(&mut self, stream: &mut TcpStream, debug_mode: &bool) {
+ match self.location.as_ref() {
+ Some(_) => {
+ self.read_metadata(debug_mode);
+ match self.metadata {
+ Some(ref mut metadata) => {
+ if Metadata::is_symlink(metadata) {
+ //Unix-Windows Problem
+ println!("\n\tError: Symlink Transfers've not Supported yet\n");
+ //self.open_file(debug_mode);
+ //self.send_file(stream, &(100 as u8),debug_mode);
+ } else if Metadata::is_file(metadata) {
+ self.open_file(debug_mode);
+ self.send_file(stream, &(101_u8), debug_mode);
+ } else if Metadata::is_dir(metadata) {
+ //path recognition and creation on the other side
+ //std:path
+ println!("\n\tError: Folder Transfers've not Supported yet\n");
+ //self.open_file(debug_mode);
+ //self.send_file(stream, &(102 as u8),debug_mode);
+ } else {
+ println!(
+ "Error: Undefined Type -> {}",
+ self.location.as_ref().unwrap()
+ );
+ }
}
- "0" =>
- {
- DebugMode::Off
- }
- input =>
- {
- println!("Error: Give Valid Input, You Gave : {}", input);
- panic!()
+ None => {
+ println!(
+ "Error: Read Metadata -> {}",
+ self.location.as_ref().unwrap()
+ );
}
+ }
}
+ None => {
+ println!("Error: Reading Operations -> {:#?}", &self.location);
+ panic!();
+ }
+ }
}
-fn main()
- {
- //DONT FORGET
- //First we should check folder structure and validation then make connection.
- println!("Hello, world!");
+ fn writing_operations(&mut self, stream: &mut TcpStream, debug_mode: &bool) {
+ self.write_file(stream, debug_mode);
+ self.cleaning();
+ }
+ fn cleaning(&mut self) {
+ self.location = self.sign.clone();
+ self.size_current = 0;
+ }
+ fn read_metadata(&mut self, debug_mode: &bool) {
+ let path = PathBuf::from(self.location.as_ref().unwrap());
+ if path.is_symlink() {
+ match path.symlink_metadata() {
+ Ok(metadata) => {
+ self.metadata = Some(metadata);
+ }
+ Err(err_val) => {
+ println!(
+ "Error: Symlink Metadata -> {:#?} | Error: {}",
+ &self.location, err_val
+ );
+ }
+ }
+ } else {
+ match fs::metadata(self.location.as_ref().unwrap()) {
+ Ok(metadata) => {
+ self.metadata = Some(metadata);
+ if *debug_mode {
+ println!("Done: Read Metadata -> {:#?}", self.metadata);
+ }
+ }
+ Err(err_val) => {
+ println!(
+ "Error: Read Metadata -> {} | Error: {}",
+ &self.location.as_ref().unwrap(),
+ err_val
+ );
+ }
+ }
+ }
+ }
+ fn open_file(&mut self, debug_mode: &bool) {
+ match File::options()
+ .read(true)
+ .write(true)
+ .open(self.location.as_ref().unwrap())
+ {
+ Ok(file) => {
+ self.file = Some(file);
+ if *debug_mode {
+ println!("Done : Open File -> {:#?}", self.file);
+ }
+ }
+ Err(err_val) => {
+ println!(
+ "Error: Open File -> {} | Error: {}",
+ self.location.as_ref().unwrap(),
+ err_val
+ );
+ }
+ }
+ }
+ fn send_file(&mut self, stream: &mut TcpStream, what_type: &u8, debug_mode: &bool) {
+ self.size_total = self.metadata.as_ref().unwrap().len();
+ let mut iteration = (self.size_total / BUFFER_SIZE) + 1;
+ let total_iteration = iteration;
+ let path_buf = PathBuf::from(Path::new(self.location.as_ref().unwrap()));
+ match what_type {
+ 100 => {
+ if *debug_mode {
+ println!(
+ "Done: Symlink Detected -> {}",
+ self.location.as_ref().unwrap()
+ );
+ }
+ self.callback_validation(stream, &String::from("100"), debug_mode);
+ }
+ 101 => {
+ if *debug_mode {
+ println!("Done: File Detected -> {}", self.location.as_ref().unwrap());
+ }
+ self.callback_validation(stream, &String::from("101"), debug_mode)
+ }
+ 102 => {
+ if *debug_mode {
+ println!(
+ "Done: Folder Detected -> {}",
+ self.location.as_ref().unwrap()
+ );
+ }
+ self.callback_validation(stream, &String::from("102"), debug_mode)
+ }
+ _ => {
+ println!(
+ "Error: Undefined Type Detected ->{}",
+ self.location.as_ref().unwrap()
+ );
+ return;
+ }
+ }
+ self.callback_validation(stream, &(self.size_total.to_string()), debug_mode);
+ self.callback_validation(
+ stream,
+ &path_buf.file_name().unwrap().to_str().unwrap().to_string(),
+ debug_mode,
+ );
- let mut data = FileInfo
- {
- file:None,
- location:take_arg(),
- size_current:0 as usize,
- metadata:None,
- };
- match &take_string("Input: Server 's', Client 'c'".to_string())[0..1]
- {
- "s" =>
- {
- Connection::server
- (Connection::Server(take_string("Input: Server Stream IP Address".to_string()),
- take_string("Input: Server Stream Port Address".to_string())),
- &mut data, DebugMode::debug_mode(debug_mod()));
- },
- "c" =>
- {
- Connection::client
- (Connection::Client(take_string("Input: Server IP Address to Connect".to_string()),
- take_string("Input: Server Port Address to Connect".to_string())),
- &mut data, DebugMode::debug_mode(debug_mod()));
- }
- input =>
- {
- println!("Error: Give Valid Input, You Gave : {}", input);
- return;
- }
+ self.show_info(&iteration, debug_mode);
+ while iteration != 0 {
+ iteration -= 1;
+ let mut buffer = [0u8; BUFFER_SIZE as usize];
+ if iteration != 0 {
+ self.read_exact(&mut buffer, debug_mode);
+ } else {
+ self.read_exact(
+ &mut buffer[..(self.size_total % BUFFER_SIZE) as usize],
+ debug_mode,
+ );
}
+ if *debug_mode {
+ println!("Read Data = {:#?}", buffer);
+ }
+ self.send_exact(&buffer, stream, debug_mode);
+ self.show_progress(iteration, total_iteration);
+ }
}
+ fn callback_validation(&mut self, stream: &mut TcpStream, data: &String, debug_mode: &bool) {
+ let mut data_vec: Vec = data.as_bytes().to_vec();
+ let mut terminator_vec: Vec = vec![b'\n'; BUFFER_SIZE as usize - data_vec.len()];
+ data_vec.append(&mut terminator_vec);
+ drop(terminator_vec);
+ let mut data_exact: [u8; BUFFER_SIZE as usize] = [b'\n'; BUFFER_SIZE as usize];
+ data_exact.swap_with_slice(data_vec[..].as_mut());
+ drop(data_vec);
+ self.send_exact(&data_exact, stream, debug_mode);
+ let mut data_exact_check: [u8; BUFFER_SIZE as usize] = [b'\n'; BUFFER_SIZE as usize];
+ self.recv_exact(&mut data_exact_check, stream, debug_mode);
+ if data_exact_check == data_exact {
+ if *debug_mode {
+ println!("Done: Callback -> {}", self.location.as_ref().unwrap());
+ println!("{:#?} ", data_exact_check);
+ }
+ } else {
+ println!("Error: Callback -> {}", self.location.as_ref().unwrap());
+ println!("{:#?} ", data_exact_check);
+ panic!()
+ }
+ }
+ fn read_exact(&mut self, buffer: &mut [u8], debug_mode: &bool) {
+ match self.file.as_ref().unwrap().read_exact(buffer) {
+ Ok(_) => {
+ if *debug_mode {
+ println!("Done: Read Bytes -> {}", self.location.as_ref().unwrap());
+ println!("{:#?}", buffer);
+ }
+ }
+ Err(err_val) => {
+ println!(
+ "Error: Read Bytes -> {} | Error: {}",
+ self.location.as_ref().unwrap(),
+ err_val
+ );
+ panic!()
+ }
+ }
+ }
+ fn send_exact(&mut self, buffer: &[u8], stream: &mut TcpStream, debug_mode: &bool) {
+ let mut stream_writer = BufWriter::new(stream.try_clone().unwrap());
+ match stream_writer.write_all(buffer) {
+ Ok(_) => {
+ self.size_current += buffer.len() as u64;
+ if *debug_mode {
+ println!("Done: Send Bytes -> {:#?}", self.location);
+ println!("{:#?}", buffer);
+ }
+ }
+ Err(err_val) => {
+ println!(
+ "Error: Send Bytes -> {:#?} | Error: {}",
+ self.location, err_val
+ );
+ panic!();
+ }
+ }
+ match stream_writer.flush() {
+ Ok(_) => {
+ if *debug_mode {
+ println!("Done: Flush -> {:#?}", self.location);
+ }
+ }
+ Err(err_val) => {
+ println!("Error: Flush -> {:#?} | Error: {}", self.location, err_val);
+ panic!()
+ }
+ }
+ }
+ fn recv_exact(&mut self, buffer: &mut [u8], stream: &mut TcpStream, debug_mode: &bool) {
+ match stream.read_exact(buffer) {
+ Ok(_) => {
+ self.size_current += buffer.len() as u64;
+ if *debug_mode {
+ println!("Done: Receive Bytes -> {:#?}", self.location);
+ println!("{:#?}", buffer);
+ }
+ }
+ Err(err_val) => {
+ println!(
+ "Error: Receive Bytes -> {:#?} | Error: {}",
+ self.location, err_val
+ );
+ panic!();
+ }
+ }
+ }
+ fn forge_file(&mut self, location: String, debug_mode: &bool) {
+ //dont forget
+ //directory recognition required for received location
+ match self.location.as_ref() {
+ Some(self_location) => {
+ let mut path = PathBuf::from(&self_location);
+ path.push(location);
+ self.forge_folder(self_location.clone(), debug_mode);
+ self.location = Some(path.to_str().unwrap().to_string());
+ }
+ None => {
+ self.location = Some(location);
+ }
+ }
+ match File::create(self.location.as_ref().unwrap()) {
+ Ok(file) => {
+ if *debug_mode {
+ println!("Done Forge File -> {:#?}", file);
+ }
+ }
+ Err(err_val) => {
+ println!(
+ "Error: Forge File -> {:#?} | Error: {}",
+ self.location.as_ref(),
+ err_val
+ );
+ }
+ }
+ }
+ fn forge_folder(&mut self, location: String, debug_mode: &bool) {
+ match fs::create_dir_all(&location) {
+ Ok(_) => {
+ if *debug_mode {
+ println!("Done: Forge Folder -> {}", &location);
+ }
+ }
+ Err(err_val) => {
+ println!("Error: Forge Folder -> {} | Error: {}", location, err_val);
+ }
+ }
+ }
+ fn callback_recv(&mut self, stream: &mut TcpStream, debug_mode: &bool) -> String {
+ let mut buffer: [u8; BUFFER_SIZE as usize] = [0; BUFFER_SIZE as usize];
+ self.recv_exact(&mut buffer, stream, debug_mode);
+ if *debug_mode {
+ println!("Done: Callback -> {:#?}", self.location);
+ println!("{:#?} ", buffer);
+ }
+ let data = String::from_utf8(
+ buffer
+ .split(|element| *element == b'\n')
+ .next()
+ .unwrap()
+ .to_vec(),
+ )
+ .unwrap();
+ if *debug_mode {
+ println!("Done: Split -> {:#?}", self.location);
+ println!("{:#?}", data);
+ }
+ self.send_exact(&buffer, stream, debug_mode);
+ data
+ }
+ fn save_exact(&mut self, buffer: &[u8], debug_mode: &bool) {
+ let mut file_writer = BufWriter::new(self.file.as_ref().unwrap());
+ if *debug_mode {
+ println!("{:#?}", file_writer);
+ }
+ match file_writer.write_all(buffer) {
+ Ok(_) => {
+ if *debug_mode {
+ println!(
+ "Done: Write -> {} | {} bytes",
+ self.location.as_ref().unwrap(),
+ self.size_current
+ );
+ println!("{:#?}", buffer);
+ }
+ }
+ Err(err_val) => {
+ println!(
+ "Error: Write -> {} | Error: {}",
+ self.location.as_ref().unwrap(),
+ err_val
+ );
+ panic!();
+ }
+ }
+ match file_writer.flush() {
+ Ok(_) => {
+ if *debug_mode {
+ println!("Done: Flush -> {}", self.location.as_ref().unwrap());
+ }
+ }
+ Err(err_val) => {
+ println!(
+ "Error: Flush -> {} | Error: {}",
+ self.location.as_ref().unwrap(),
+ err_val
+ );
+ panic!();
+ }
+ }
+ }
+ fn write_file(&mut self, stream: &mut TcpStream, debug_mode: &bool) {
+ let what_type: FileType = FileType::what_type(
+ self.callback_recv(stream, debug_mode)
+ .parse::()
+ .unwrap(),
+ self,
+ debug_mode,
+ );
+ self.size_total = self.callback_recv(stream, debug_mode).parse().unwrap();
+ let location: String = self.callback_recv(stream, debug_mode);
+ self.file_forger(what_type, location, debug_mode);
+ self.open_file(debug_mode);
+ let mut iteration: u64 = (self.size_total / BUFFER_SIZE) + 1;
+ let total_iteration = iteration;
+ self.show_info(&iteration, debug_mode);
+ while iteration != 0 {
+ iteration -= 1;
+ let mut buffer = [0u8; BUFFER_SIZE as usize];
+ self.recv_exact(&mut buffer, stream, debug_mode);
+ if iteration != 0 {
+ self.save_exact(&buffer, debug_mode);
+ } else {
+ self.save_exact(
+ &buffer[..(self.size_total % BUFFER_SIZE) as usize],
+ debug_mode,
+ );
+ }
+ self.show_progress(iteration, total_iteration);
+ }
+ }
+ fn file_forger(&mut self, file_type: FileType, location: String, debug_mode: &bool) {
+ match file_type {
+ FileType::Symlink => {
+ self.forge_file(location, debug_mode);
+ }
+ FileType::File => {
+ self.forge_file(location, debug_mode);
+ }
+ FileType::Folder => {
+ self.forge_file(location, debug_mode);
+ }
+ }
+ }
+ fn show_info(&mut self, iteration: &u64, debug_mode: &bool) {
+ println!("File = {}", self.location.as_ref().unwrap());
+ println!("Size = {}", self.size_total);
+ if *debug_mode {
+ println!("Iteration = {}", iteration);
+ }
+ }
+ fn show_progress(&mut self, iteration: u64, total_iteration: u64) {
+ if iteration % 10 == 0 {
+ let progress: u8 =
+ 100_u8 - ((iteration as f64 / total_iteration as f64) * 100_f64) as u8;
+ if progress != self.progress {
+ self.progress = progress;
+ println!("%{}", self.progress);
+ }
+ }
+ }
+}
+enum Connection {
+ Server(String, String),
+ Client(String, String),
+}
+
+impl Connection {
+ fn server(self, file_info: &mut FileInfo, user_environment: &UserEnvironment) {
+ print!("Server -> ");
+ if user_environment.debug {
+ println!("{:#?}", user_environment);
+ println!("{:#?}", file_info);
+ }
+ let ip: String;
+ let port: String;
+ let address: String;
+ match self {
+ Connection::Server(in1, in2) => {
+ ip = in1.trim_end().to_string();
+ port = in2.trim_end().to_string();
+ address = format!("{}:{}", ip, port);
+ println!("{}", address);
+ }
+ _ => return,
+ }
+ let socket = TcpListener::bind(address);
+ for stream in socket.expect("Error: Can't Check Connections").incoming() {
+ match stream {
+ Ok(mut stream) => {
+ println!("Connected");
+ send_or_receive(
+ file_info,
+ &mut stream,
+ &user_environment.debug,
+ user_environment,
+ );
+ }
+ Err(e) => {
+ println!("Error: Can't Visit Stream -> {}", e);
+ return;
+ }
+ }
+ }
+ }
+ fn client(self, file_info: &mut FileInfo, user_environment: &UserEnvironment) {
+ print!("Client -> ");
+ if user_environment.debug {
+ println!("{:#?}", user_environment);
+ println!("{:#?}", file_info);
+ }
+ let ip: String;
+ let port: String;
+ let address: String;
+ match self {
+ Connection::Client(in1, in2) => {
+ ip = in1.trim_end().to_string();
+ port = in2.trim_end().to_string();
+ address = format!("{}:{}", ip, port);
+ println!("{}", address);
+ }
+ _ => return,
+ }
+ match TcpStream::connect(address) {
+ Ok(mut stream) => {
+ println!("Connected");
+ send_or_receive(
+ file_info,
+ &mut stream,
+ &user_environment.debug,
+ user_environment,
+ );
+ }
+ Err(e) => {
+ println!("Error: Connection -> {}", e);
+ }
+ }
+ }
+}
+fn send_or_receive(
+ file_info: &mut FileInfo,
+ stream: &mut TcpStream,
+ debug_mode: &bool,
+ user_environment: &UserEnvironment,
+) {
+ match user_environment.send {
+ true => {
+ let start_time = Instant::now();
+ FileInfo::reading_operations(file_info, stream, debug_mode);
+ let finish_time = Instant::now();
+ println!("Done: Transfer");
+ println!(
+ "Passed: Total -> {:#?}",
+ finish_time.duration_since(start_time)
+ );
+ }
+ false => {
+ let start_time = Instant::now();
+ FileInfo::writing_operations(file_info, stream, debug_mode);
+ let finish_time = Instant::now();
+ println!("Done: Transfer");
+ println!(
+ "Passed: Total -> {:#?}",
+ finish_time.duration_since(start_time)
+ );
+ }
+ }
+}
+fn take_args(mut user_environment: UserEnvironment) -> Option {
+ let env_args: Vec = env::args().collect();
+ if env_args.len() > 16 {
+ println!(
+ "Error: Too Many Arguments, You Gave {} Arguments",
+ env_args.len()
+ );
+ return None;
+ }
+ let mut i = 1;
+ while i < env_args.len() {
+ match env_args[i].as_str() {
+ "--ip" | "-i" => {
+ user_environment.ip = env_args[i + 1].parse().unwrap();
+ i += 1;
+ }
+ "--port" | "-p" => {
+ user_environment.port = env_args[i + 1].parse().unwrap();
+ i += 1;
+ }
+ "--location" | "-l" => {
+ user_environment.location = Some(env_args[i + 1].parse().unwrap());
+ i += 1;
+ }
+ "--server" | "-sv" => {
+ user_environment.server = true;
+ }
+ "--client" | "-cl" => {
+ user_environment.server = false;
+ }
+ "--send" | "-s" => {
+ user_environment.send = true;
+ }
+ "--receive" | "-r" => {
+ user_environment.send = false;
+ }
+ "--debug" | "-d" => {
+ user_environment.debug = true;
+ }
+ "--help" | "-h" => {
+ show_help();
+ return None;
+ }
+ err => {
+ println!("Error: Invalid Argument, You Gave {}", err);
+ return None;
+ }
+ }
+ i += 1;
+ }
+ Some(user_environment)
+}
+fn show_help() {
+ println!("\n\n\n");
+ println!(" Arguments | Details | Defaults");
+ println!("----------------------------------------------------------------------");
+ println!(" -i -> --ip | Specifies IP Address | 127.0.0.1");
+ println!(" -p -> --port | Specifies Port Address | 2121");
+ println!(" -l -> --location | Specifies Location Address | Same as Program");
+ println!(" -sv -> --server | Starts as a Server | False");
+ println!(" -cl -> --client | Starts as a Client | True");
+ println!(" -s -> --send | Starts as a Sender | False");
+ println!(" -r -> --receive | Starts as a Receiver | True");
+ println!(" -d -> --debug | Starts in Debug Mode | False");
+ println!(" -h -> --help | Shows Help | False");
+ println!("\n\n\n");
+}
+fn main() {
+ //DONT FORGET
+ //First we should check folder structure and validation then make connection.
+ println!("Hello, world!");
+ let mut file_info: FileInfo = FileInfo::new();
+ let user_environment: UserEnvironment = match take_args(UserEnvironment::new()) {
+ Some(usr_env) => usr_env,
+ None => {
+ return;
+ }
+ };
+ file_info.pass_user_environment(&user_environment);
+ match user_environment.server {
+ true => {
+ Connection::server(
+ Connection::Server(
+ user_environment.ip.to_string(),
+ user_environment.port.to_string(),
+ ),
+ &mut file_info,
+ &user_environment,
+ );
+ }
+ false => {
+ Connection::client(
+ Connection::Client(
+ user_environment.ip.to_string(),
+ user_environment.port.to_string(),
+ ),
+ &mut file_info,
+ &user_environment,
+ );
+ }
+ }
+}