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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
extern crate hostname;
extern crate username;

use hostname::get_hostname;
use username::get_user_name;

use std::{
    fs::File,
    io::{self, Error, ErrorKind, Read, Write},
    net::TcpStream,
    process,
    time::Duration,
};

#[derive(Debug)]
pub struct LprConnection {
    stream: TcpStream,
    verbose: bool,
}

#[derive(Debug)]
pub enum LprError
{
    IoError(io::Error),
    AckError,
}

impl From<io::Error> for LprError
{
    fn from(err: io::Error) -> LprError
    {
        LprError::IoError(err)
    }
}

impl LprConnection {
    pub fn new(ip_str: &str, timeout_ms: u64) -> Result<LprConnection, LprError> {
        let stream = TcpStream::connect(format!("{}:515", ip_str))?;
        stream
            .set_read_timeout(Some(Duration::from_millis(timeout_ms)))?;
        Ok(LprConnection {
            stream,
            verbose: false,
        })
    }

    pub fn verbose(&mut self, verbose: bool) {
        self.verbose = verbose;
    }

    pub fn status(mut self) -> io::Result<String> {
        let bytes_written = self.stream.write(&[4, b'\n'])?;
        match bytes_written {
            2 => {
                let mut buf: Vec<u8> = Vec::new();
                self.stream.read_to_end(&mut buf)?;
                let buf_str = String::from_utf8_lossy(&buf).to_string();
                let split: Vec<&str> = buf_str.split("\n\n").collect();
                Ok(split[0].to_string())
            }
            _ => Err(Error::new(
                ErrorKind::Interrupted,
                "not all bytes have been written",
            )),
        }
    }

    fn send_and_wait_for_ack(&mut self, data: &[u8], description: &str) -> Result<(), LprError> {
        if self.verbose {
            print!("Sending {}.. ", description);
        }
        self.stream.write_all(data)?;

        let mut buf = [0; 1];
        self.stream.read_exact(&mut buf)?;
        if self.verbose {
            println!("acknowledged");
        }
        if buf[0] != 0 {
            return Err(LprError::AckError);
        }
        Ok(())
    }

    fn generate_control_file_and_name(&self) -> (String, String) {
        let host = match get_hostname() {
            Some(name) => name,
            None => "lpr-host".to_string(),
        };
        let user = match get_user_name() {
            Ok(name) => name,
            Err(_) => "lpr-user".to_string(),
        };
        let name = format!("fA{}{}", process::id() % 1000, host);
        (format!("H{}\nP{}\nld{}\n", host, user, name), name)
    }

    pub fn print_file(&mut self, path_to_file: &str) -> Result<(), LprError> {
        if self.verbose {
            print!("Priting {}.. ", &path_to_file)
        }
        let mut file = File::open(path_to_file)?;
        let mut buf: Vec<u8> = Vec::with_capacity(8096);
        let file_size = file.read_to_end(&mut buf)?;
        if self.verbose {
            println!("File Size: {:?}", file_size);
        }
        self.print(&buf)?;
        Ok(())
    }

    pub fn print_file_with_pjl_header(
        &mut self,
        path_to_file: &str,
        mut header_data: Vec<u8>,
    ) -> Result<(), LprError> {
        let mut buf: Vec<u8> = Vec::with_capacity(8096);
        let mut file = File::open(path_to_file)?;
        let mut file_buf: Vec<u8> = Vec::with_capacity(8096);
        let _file_size = file.read_to_end(&mut file_buf)?;
        buf.append(&mut header_data);
        buf.append(&mut file_buf);
        buf.append(&mut b"\x1b%-12345X@PJL EOJ\r\n\x1b%-12345X".to_vec());
        self.print(&buf)?;
        Ok(())
    }

    pub fn print(&mut self, data: &[u8]) -> Result<(), LprError> {
        let (controlfile, job_name) = self.generate_control_file_and_name();
        if self.verbose {
            println!("generated controlfile:\n{}", controlfile)
        }
        self.send_and_wait_for_ack(b"\x02lp\n", "receive job command")?;

        self.send_and_wait_for_ack(
            &format!("\x02{} c{}\n", controlfile.len(), job_name).as_bytes(),
            "receive controlfile subcommand",
        )?;

        self.send_and_wait_for_ack(&format!("{}\x00", controlfile).as_bytes(), "control file")?;

        self.send_and_wait_for_ack(
            &format!("\x03{} d{}\n", data.len(), job_name).as_bytes(),
            "receive datafile subcommand",
        )?;

        self.stream.write_all(data)?;

        self.send_and_wait_for_ack(&[0], "data file and ack")?;
        Ok(())
    }
}