From 557159a2e00508820cb7bad66822cc97c61b6de5 Mon Sep 17 00:00:00 2001 From: bnewbold Date: Sun, 29 May 2016 20:31:09 -0400 Subject: begin implementation of actual file transfer --- src/client.rs | 7 ++-- src/common.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ src/main.rs | 2 +- src/server.rs | 7 ++-- 4 files changed, 108 insertions(+), 18 deletions(-) diff --git a/src/client.rs b/src/client.rs index a2ebf27..637a216 100644 --- a/src/client.rs +++ b/src/client.rs @@ -49,10 +49,11 @@ pub fn run_client(host: &str, local_file: &str, remote_file: &str, remote_is_dir println!("\tsecret: {}", remote_secret); let mut socket = UtpSocket::connect((remote_host, remote_port)).unwrap();; + let mut stream = socket.into(); if is_recv { - common::receive_files(&mut socket, local_file, remote_is_dir); + common::sink_files(&mut stream, local_file, remote_is_dir); } else { - common::send_files(&mut socket, local_file, remote_is_dir); + common::source_files(&mut stream, local_file, remote_is_dir); } - socket.close().unwrap(); + stream.close().unwrap(); } diff --git a/src/common.rs b/src/common.rs index 8183bbf..421f3a5 100644 --- a/src/common.rs +++ b/src/common.rs @@ -3,21 +3,109 @@ extern crate utp; use std::str; use std::env; -use std::fs::File; -use std::io::Read; +use std::path::Path; +use std::fs::{self, File}; +use std::os::unix::fs::PermissionsExt; +use std::io; +use std::io::{Read, Write, BufRead, BufReader}; use std::process::exit; -use utp::{UtpSocket}; +use utp::{UtpStream}; -pub fn send_files(socket: &mut UtpSocket, file_path: &str, recursive: bool) { - assert!(!recursive); - let f = File::open(file_path).unwrap(); - unimplemented!(); +pub fn source_files(stream: &mut UtpStream, file_path: &str, recursive: bool) { + if recursive { unimplemented!(); } + let mut f = File::open(file_path).unwrap(); + let metadata = f.metadata().unwrap(); + assert!(metadata.is_file()); + let fmode: u32 = metadata.permissions().mode(); + let flen: usize = metadata.len() as usize; + + // Format as 4 digits octal, left-padding with zero + let line = format!("C{:0<4o} {} {}\n", fmode, flen, file_path); + stream.write_all(line.as_bytes()).unwrap(); + + let mut byte_buf = [0; 1]; + stream.read_exact(&mut byte_buf).unwrap(); + let reply = byte_buf[0]; + match reply { + 0 => {}, // Success, pass + 1 | 2 => { // Warning + unimplemented!(); + }, + _ => { panic!("Unexpected status char!") }, + }; + + let mut buf = [0; 4096]; + let mut sent: usize = 0; + while sent < flen { + let rlen = f.read(&mut buf).unwrap(); + assert!(rlen > 0); + let mut wbuf = &mut buf[..rlen]; + stream.write_all(&wbuf).unwrap(); + sent += rlen; + } + // f.close(); XXX: + stream.read_exact(&mut byte_buf).unwrap(); + let reply = byte_buf[0]; + match reply { + 0 => {}, // Success, pass + 1 | 2 => { // Warning + unimplemented!(); + }, + _ => { panic!("Unexpected status char!") }, + }; +} + +fn raw_read_line(stream: &mut UtpStream) -> io::Result { + + let mut s = String::new(); + let mut byte_buf = [0]; + loop { + stream.read_exact(&mut byte_buf).unwrap(); + if byte_buf[0] == '\n' as u8 { + return Ok(s); + } + s.push(byte_buf[0] as char); + } } -pub fn receive_files(socket: &mut UtpSocket, file_path: &str, recursive: bool) { - assert!(!recursive); - let f = File::create(file_path).unwrap(); +// TODO: it would be nice to be able to do BufReader/BufWriter on UtpStream. This would require +// implementations of Read and Write on immutable references to UtpStream (a la TcpStream, File, et +// al) +pub fn sink_files(stream: &mut UtpStream, file_path: &str, recursive: bool) { + if recursive { unimplemented!(); } + let mut f = File::create(file_path).unwrap(); + + let mut byte_buf = [0; 1]; + let mut buf = [0; 4096]; + stream.read_exact(&mut byte_buf).unwrap(); + let msg_type = byte_buf[0]; + match msg_type as char { + 'C' => {} // pass + 'D' => { unimplemented!(); }, + 'E' => { unimplemented!(); }, + 'T' => { unimplemented!(); }, + _ => { panic!(format!("Unexpected message type: {}", msg_type)); }, + }; + let line = raw_read_line(stream).unwrap(); + let line: Vec<&str> = line.split_whitespace().collect(); + assert!(line.len() == 3); + let fmode: u32 = u32::from_str_radix(line[0], 8).unwrap(); + let flen: usize = line[1].parse::().unwrap(); + let fpath = Path::new(line[2]); + + f.set_len(flen as u64).unwrap(); + fs::set_permissions(file_path, PermissionsExt::from_mode(fmode)).unwrap(); - //f.set_len(); + let mut received: usize = 0; + while received < flen { + let rlen = stream.read(&mut buf).unwrap(); + assert!(rlen > 0); + let mut wbuf = &mut buf[..rlen]; + f.write_all(&wbuf).unwrap(); + received += rlen; + } + f.sync_all().unwrap(); + // f.close(); XXX: closes automatically? + stream.write(&[0]).unwrap(); } diff --git a/src/main.rs b/src/main.rs index bf2ceba..f916958 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ // XXX: re-enable these warnings -#![allow(unused_imports, unused_variables)] +#![allow(unused_imports, unused_variables, unused_mut)] extern crate getopts; extern crate utp; diff --git a/src/server.rs b/src/server.rs index e60c054..4c590be 100644 --- a/src/server.rs +++ b/src/server.rs @@ -47,13 +47,14 @@ fn run_server(path: &str, is_recv: bool, recursive: bool) { let (mut socket, _src) = listener.accept().unwrap(); println!("Got connection from {}", socket.peer_addr().unwrap()); + let mut stream = socket.into(); if is_recv { - common::receive_files(&mut socket, path, recursive); + common::sink_files(&mut stream, path, recursive); } else { - common::send_files(&mut socket, path, recursive); + common::source_files(&mut stream, path, recursive); } - socket.close().unwrap(); + stream.close().unwrap(); } fn usage_server(opts: Options) { -- cgit v1.2.3