aboutsummaryrefslogtreecommitdiffstats
path: root/src/common.rs
blob: 175c4b8e758ed5af6e390ea9a4f8f5106eb8e130 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

use std::str;
use std::env;
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 std::net;

// This function is an ugly hack fall-back. It tries to find public IP address of this machine by
// creating an outdoing connection
pub fn get_local_ip() -> io::Result<net::IpAddr> {
    
    let dummy = try!(net::TcpListener::bind("0.0.0.0:0"));
    let addr = try!(dummy.local_addr()).ip();
    drop(dummy);
    Ok(addr)
}

pub fn source_files<S: Read + Write>(stream: &mut S, file_path: &str, recursive: bool) {
    println!("SOURCE FILE: {}", file_path);
    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();
    println!("{}", line);

    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;
        //println!("sent: {}", sent);
    }
    // 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<S: Read + Write>(stream: &mut S) -> io::Result<String> {

    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);
    }
}

// TODO: it would be nice to be able to do BufReader/BufWriter on stream. This would require
// implementations of Read and Write on immutable references to stream (a la TcpStream, File, et
// al)
pub fn sink_files<S: Read + Write>(stream: &mut S, file_path: &str, recursive: bool) {
    println!("SINK FILE: {}", file_path);
    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' => {
            println!("Going to create!");
        },
        'D' => { unimplemented!(); },
        'E' => { unimplemented!(); },
        'T' => { unimplemented!(); },
        _   => { panic!(format!("Unexpected message type: {}", msg_type)); },
    };
    let line = raw_read_line(stream).unwrap();
    println!("got msg: {}", line);
    stream.write(&[0]).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::<usize>().unwrap();
    let fpath = Path::new(line[2]);

    // TODO: I've disabled set_len; is this best practice? scp doesn't do it.
    //f.set_len(flen as u64).unwrap();
    fs::set_permissions(file_path, PermissionsExt::from_mode(fmode)).unwrap();

    let mut received: usize = 0;
    while received < flen {
        let rlen = stream.read(&mut buf).unwrap();
        //println!("recieved: {}", rlen);
        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();
}