1use 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#[derive(EnumAsInner)]
22pub enum PayloadKind {
23 JobSource(IppJobSource),
25 ReceivedData(NamedTempFile),
27}
28
29pub struct IppRequestResponse {
31 header: IppHeader,
33 attributes: IppAttributes,
35 payload: Option<PayloadKind>,
37}
38
39impl IppRequestResponse {
40 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 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 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 pub fn header(&self) -> &IppHeader {
100 &self.header
101 }
102
103 pub fn header_mut(&mut self) -> &mut IppHeader {
105 &mut self.header
106 }
107
108 pub fn attributes(&self) -> &IppAttributes {
110 &self.attributes
111 }
112
113 pub fn attributes_mut(&mut self) -> &mut IppAttributes {
115 &mut self.attributes
116 }
117
118 pub fn payload(&self) -> &Option<PayloadKind> {
120 &self.payload
121 }
122
123 pub fn payload_mut(&mut self) -> &mut Option<PayloadKind> {
125 &mut self.payload
126 }
127
128 pub fn add_payload(&mut self, payload: IppJobSource) {
130 self.payload = Some(PayloadKind::JobSource(payload))
131 }
132
133 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 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}