1use std::io::{self, Read, Write};
2
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use bytes::{Bytes, BytesMut};
5use futures::{try_ready, Async, Poll, Stream};
6use num_traits::FromPrimitive;
7use tokio::io::AsyncRead;
8
9pub use crate::{
10 attribute::{IppAttribute, IppAttributeGroup, IppAttributes},
11 builder::{
12 CreateJobBuilder, GetPrinterAttributesBuilder, IppOperationBuilder, PrintJobBuilder, SendDocumentBuilder,
13 },
14 ipp::{IppVersion, Operation, StatusCode},
15 parser::{AsyncIppParser, IppParser, ParseError},
16 request::{IppRequestResponse, PayloadKind},
17 value::IppValue,
18};
19
20pub mod attribute;
21pub mod builder;
22pub mod ipp;
23pub mod operation;
24pub mod parser;
25pub mod request;
26pub mod value;
27
28pub struct IppJobSource {
30 inner: Box<AsyncRead + Send>,
31 buffer: Vec<u8>,
32}
33
34impl IppJobSource {
35 const CHUNK_SIZE: usize = 32768;
36}
37
38impl Stream for IppJobSource {
39 type Item = Bytes;
40 type Error = io::Error;
41
42 fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
43 let size = try_ready!(self.inner.poll_read(&mut self.buffer));
44 if size > 0 {
45 Ok(Async::Ready(Some(self.buffer[0..size].into())))
46 } else {
47 Ok(Async::Ready(None))
48 }
49 }
50}
51
52impl<T> From<T> for IppJobSource
53where
54 T: 'static + AsyncRead + Send,
55{
56 fn from(r: T) -> Self {
58 IppJobSource {
59 inner: Box::new(r),
60 buffer: vec![0; IppJobSource::CHUNK_SIZE],
61 }
62 }
63}
64
65pub(crate) trait IppWriter {
66 fn write(&self, writer: &mut Write) -> io::Result<usize>;
67}
68
69pub(crate) trait IppReadExt: Read {
71 fn read_string(&mut self, len: usize) -> std::io::Result<String> {
72 Ok(String::from_utf8_lossy(&self.read_bytes(len)?).to_string())
73 }
74
75 fn read_bytes(&mut self, len: usize) -> std::io::Result<Bytes> {
76 let mut buf = BytesMut::with_capacity(len);
77 buf.resize(len, 0);
78 self.read_exact(&mut buf)?;
79
80 Ok(buf.freeze())
81 }
82}
83impl<R: io::Read + ?Sized> IppReadExt for R {}
84
85#[derive(Clone, Debug)]
87pub struct IppHeader {
88 pub version: IppVersion,
90 pub operation_status: u16,
92 pub request_id: u32,
94}
95
96impl IppHeader {
97 pub fn from_reader(reader: &mut Read) -> Result<IppHeader, ParseError> {
99 let retval = IppHeader::new(
100 IppVersion::from_u16(reader.read_u16::<BigEndian>()?).ok_or_else(|| ParseError::InvalidVersion)?,
101 reader.read_u16::<BigEndian>()?,
102 reader.read_u32::<BigEndian>()?,
103 );
104 Ok(retval)
105 }
106
107 pub fn new(version: IppVersion, operation_status: u16, request_id: u32) -> IppHeader {
109 IppHeader {
110 version,
111 operation_status,
112 request_id,
113 }
114 }
115
116 pub fn operation(&self) -> Result<Operation, StatusCode> {
118 Operation::from_u16(self.operation_status).ok_or(StatusCode::ServerErrorOperationNotSupported)
119 }
120}
121
122impl IppWriter for IppHeader {
123 fn write(&self, writer: &mut Write) -> io::Result<usize> {
125 writer.write_u16::<BigEndian>(self.version as u16)?;
126 writer.write_u16::<BigEndian>(self.operation_status)?;
127 writer.write_u32::<BigEndian>(self.request_id)?;
128
129 Ok(8)
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use std::io::Cursor;
136
137 use super::*;
138
139 #[test]
140 fn test_read_header_ok() {
141 let data = &[0x01, 0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
142
143 let header = IppHeader::from_reader(&mut Cursor::new(data));
144 assert!(header.is_ok());
145
146 let header = header.ok().unwrap();
147 assert_eq!(header.version, IppVersion::Ipp11);
148 assert_eq!(header.operation_status, 0x1122);
149 assert_eq!(header.request_id, 0x33445566);
150 }
151
152 #[test]
153 fn test_read_header_error() {
154 let data = &[0xff, 0, 0, 0, 0, 0, 0, 0];
155
156 let header = IppHeader::from_reader(&mut Cursor::new(data));
157 assert!(header.is_err());
158 if let Some(ParseError::InvalidVersion) = header.err() {
159 } else {
160 assert!(false);
161 }
162 }
163
164 #[test]
165 fn test_write_header() {
166 let header = IppHeader::new(IppVersion::Ipp21, 0x1234, 0xaa55aa55);
167 let mut buf = Vec::new();
168 assert!(header.write(&mut Cursor::new(&mut buf)).is_ok());
169 assert_eq!(buf, vec![0x02, 0x01, 0x12, 0x34, 0xaa, 0x55, 0xaa, 0x55]);
170 }
171}