ipp_proto/
request.rs

1//!
2//! IPP request
3//!
4use std::io::{self, Cursor, Write};
5
6use bytes::Bytes;
7use enum_as_inner::EnumAsInner;
8use futures::Stream;
9use log::debug;
10use tempfile::NamedTempFile;
11
12use crate::{
13    attribute::*,
14    ipp::{DelimiterTag, IppVersion, Operation},
15    parser::IppParseResult,
16    value::*,
17    IppHeader, IppJobSource, IppWriter, StatusCode,
18};
19
20/// Payload type inside the IppRequestResponse
21#[derive(EnumAsInner)]
22pub enum PayloadKind {
23    /// Job source for client side
24    JobSource(IppJobSource),
25    /// Received data for server side
26    ReceivedData(NamedTempFile),
27}
28
29/// IPP request/response struct
30pub struct IppRequestResponse {
31    /// IPP header
32    header: IppHeader,
33    /// IPP attributes
34    attributes: IppAttributes,
35    /// Optional payload after IPP-encoded stream (for example binary data for Print-Job operation)
36    payload: Option<PayloadKind>,
37}
38
39impl IppRequestResponse {
40    /// Create new IPP request for the operation and uri
41    pub fn new(version: IppVersion, operation: Operation, uri: Option<&str>) -> IppRequestResponse {
42        let hdr = IppHeader::new(version, operation as u16, 1);
43        let mut retval = IppRequestResponse {
44            header: hdr,
45            attributes: IppAttributes::new(),
46            payload: None,
47        };
48
49        retval.attributes_mut().add(
50            DelimiterTag::OperationAttributes,
51            IppAttribute::new(ATTRIBUTES_CHARSET, IppValue::Charset("utf-8".to_string())),
52        );
53        retval.attributes_mut().add(
54            DelimiterTag::OperationAttributes,
55            IppAttribute::new(ATTRIBUTES_NATURAL_LANGUAGE, IppValue::NaturalLanguage("en".to_string())),
56        );
57
58        if let Some(uri) = uri {
59            retval.attributes_mut().add(
60                DelimiterTag::OperationAttributes,
61                IppAttribute::new(PRINTER_URI, IppValue::Uri(uri.replace("http", "ipp").to_string())),
62            );
63        }
64
65        retval
66    }
67
68    /// Create response from status and id
69    pub fn new_response(version: IppVersion, status: StatusCode, id: u32) -> IppRequestResponse {
70        let hdr = IppHeader::new(version, status as u16, id);
71        let mut retval = IppRequestResponse {
72            header: hdr,
73            attributes: IppAttributes::new(),
74            payload: None,
75        };
76
77        retval.attributes_mut().add(
78            DelimiterTag::OperationAttributes,
79            IppAttribute::new(ATTRIBUTES_CHARSET, IppValue::Charset("utf-8".to_string())),
80        );
81        retval.attributes_mut().add(
82            DelimiterTag::OperationAttributes,
83            IppAttribute::new(ATTRIBUTES_NATURAL_LANGUAGE, IppValue::NaturalLanguage("en".to_string())),
84        );
85
86        retval
87    }
88
89    /// Create IppRequestResponse from parse result
90    pub fn from_parse_result(result: IppParseResult) -> IppRequestResponse {
91        IppRequestResponse {
92            header: result.header,
93            attributes: result.attributes,
94            payload: result.payload,
95        }
96    }
97
98    /// Get IPP header
99    pub fn header(&self) -> &IppHeader {
100        &self.header
101    }
102
103    /// Get mutable IPP header
104    pub fn header_mut(&mut self) -> &mut IppHeader {
105        &mut self.header
106    }
107
108    /// Get attributes
109    pub fn attributes(&self) -> &IppAttributes {
110        &self.attributes
111    }
112
113    /// Get attributes
114    pub fn attributes_mut(&mut self) -> &mut IppAttributes {
115        &mut self.attributes
116    }
117
118    /// Get payload
119    pub fn payload(&self) -> &Option<PayloadKind> {
120        &self.payload
121    }
122
123    /// Get mutable payload
124    pub fn payload_mut(&mut self) -> &mut Option<PayloadKind> {
125        &mut self.payload
126    }
127
128    /// Set payload
129    pub fn add_payload(&mut self, payload: IppJobSource) {
130        self.payload = Some(PayloadKind::JobSource(payload))
131    }
132
133    /// Serialize request into the binary stream (TCP)
134    pub fn write(&mut self, writer: &mut Write) -> io::Result<usize> {
135        let mut retval = self.header.write(writer)?;
136
137        retval += self.attributes.write(writer)?;
138
139        debug!("Wrote {} bytes IPP stream", retval);
140
141        Ok(retval)
142    }
143
144    /// Convert request/response into Stream
145    pub fn into_stream(self) -> Box<dyn Stream<Item = Bytes, Error = io::Error> + Send + 'static> {
146        let mut cursor = Cursor::new(Vec::with_capacity(1024));
147        let _ = self
148            .header
149            .write(&mut cursor)
150            .and_then(|_| self.attributes.write(&mut cursor));
151
152        let headers = futures::stream::once(Ok(cursor.into_inner().into()));
153
154        match self.payload {
155            Some(PayloadKind::JobSource(payload)) => Box::new(headers.chain(payload)),
156            _ => Box::new(headers),
157        }
158    }
159}