ipp_proto/
value.rs

1//!
2//! IPP value
3//!
4use std::{
5    fmt,
6    io::{self, Read, Write},
7};
8
9use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
10use bytes::Bytes;
11use enum_as_inner::EnumAsInner;
12use num_traits::FromPrimitive;
13
14use crate::{ipp::ValueTag, IppReadExt, IppWriter};
15
16/// IPP value enumeration
17#[derive(Clone, Debug, PartialEq, EnumAsInner)]
18pub enum IppValue {
19    Integer(i32),
20    Enum(i32),
21    OctetString(String),
22    TextWithoutLanguage(String),
23    NameWithoutLanguage(String),
24    Charset(String),
25    NaturalLanguage(String),
26    Uri(String),
27    RangeOfInteger {
28        min: i32,
29        max: i32,
30    },
31    Boolean(bool),
32    Keyword(String),
33    ListOf(Vec<IppValue>),
34    Collection(Vec<IppValue>),
35    MimeMediaType(String),
36    DateTime {
37        year: u16,
38        month: u8,
39        day: u8,
40        hour: u8,
41        minutes: u8,
42        seconds: u8,
43        deciseconds: u8,
44        utcdir: char,
45        utchours: u8,
46        utcmins: u8,
47    },
48    MemberAttrName(String),
49    Resolution {
50        crossfeed: i32,
51        feed: i32,
52        units: i8,
53    },
54    Other {
55        tag: u8,
56        data: Bytes,
57    },
58}
59
60impl IppValue {
61    /// Convert to binary tag
62    pub fn to_tag(&self) -> ValueTag {
63        match *self {
64            IppValue::Integer(_) => ValueTag::Integer,
65            IppValue::Enum(_) => ValueTag::Enum,
66            IppValue::RangeOfInteger { .. } => ValueTag::RangeOfInteger,
67            IppValue::Boolean(_) => ValueTag::Boolean,
68            IppValue::Keyword(_) => ValueTag::Keyword,
69            IppValue::OctetString(_) => ValueTag::OctetStringUnspecified,
70            IppValue::TextWithoutLanguage(_) => ValueTag::TextWithoutLanguage,
71            IppValue::NameWithoutLanguage(_) => ValueTag::NameWithoutLanguage,
72            IppValue::Charset(_) => ValueTag::Charset,
73            IppValue::NaturalLanguage(_) => ValueTag::NaturalLanguage,
74            IppValue::Uri(_) => ValueTag::Uri,
75            IppValue::MimeMediaType(_) => ValueTag::MimeMediaType,
76            IppValue::ListOf(ref list) => list[0].to_tag(),
77            IppValue::Collection(_) => ValueTag::BegCollection,
78            IppValue::DateTime { .. } => ValueTag::DateTime,
79            IppValue::MemberAttrName(_) => ValueTag::MemberAttrName,
80            IppValue::Resolution { .. } => ValueTag::Resolution,
81            IppValue::Other { .. } => ValueTag::Unknown,
82        }
83    }
84
85    /// Read value from binary stream
86    pub fn read(vtag: u8, reader: &mut Read) -> io::Result<IppValue> {
87        let vsize = reader.read_u16::<BigEndian>()?;
88
89        let ipptag = match ValueTag::from_u8(vtag) {
90            Some(x) => x,
91            None => {
92                return Ok(IppValue::Other {
93                    tag: vtag,
94                    data: reader.read_bytes(vsize as usize)?,
95                });
96            }
97        };
98
99        match ipptag {
100            ValueTag::Integer => {
101                debug_assert_eq!(vsize, 4);
102                Ok(IppValue::Integer(reader.read_i32::<BigEndian>()?))
103            }
104            ValueTag::Enum => {
105                debug_assert_eq!(vsize, 4);
106                Ok(IppValue::Enum(reader.read_i32::<BigEndian>()?))
107            }
108            ValueTag::OctetStringUnspecified => Ok(IppValue::OctetString(reader.read_string(vsize as usize)?)),
109            ValueTag::TextWithoutLanguage => Ok(IppValue::TextWithoutLanguage(reader.read_string(vsize as usize)?)),
110            ValueTag::NameWithoutLanguage => Ok(IppValue::NameWithoutLanguage(reader.read_string(vsize as usize)?)),
111            ValueTag::Charset => Ok(IppValue::Charset(reader.read_string(vsize as usize)?)),
112            ValueTag::NaturalLanguage => Ok(IppValue::NaturalLanguage(reader.read_string(vsize as usize)?)),
113            ValueTag::Uri => Ok(IppValue::Uri(reader.read_string(vsize as usize)?)),
114            ValueTag::RangeOfInteger => {
115                debug_assert_eq!(vsize, 8);
116                Ok(IppValue::RangeOfInteger {
117                    min: reader.read_i32::<BigEndian>()?,
118                    max: reader.read_i32::<BigEndian>()?,
119                })
120            }
121            ValueTag::Boolean => {
122                debug_assert_eq!(vsize, 1);
123                Ok(IppValue::Boolean(reader.read_u8()? != 0))
124            }
125            ValueTag::Keyword => Ok(IppValue::Keyword(reader.read_string(vsize as usize)?)),
126            ValueTag::MimeMediaType => Ok(IppValue::MimeMediaType(reader.read_string(vsize as usize)?)),
127            ValueTag::DateTime => Ok(IppValue::DateTime {
128                year: reader.read_u16::<BigEndian>()?,
129                month: reader.read_u8()?,
130                day: reader.read_u8()?,
131                hour: reader.read_u8()?,
132                minutes: reader.read_u8()?,
133                seconds: reader.read_u8()?,
134                deciseconds: reader.read_u8()?,
135                utcdir: reader.read_u8()? as char,
136                utchours: reader.read_u8()?,
137                utcmins: reader.read_u8()?,
138            }),
139            ValueTag::MemberAttrName => Ok(IppValue::MemberAttrName(reader.read_string(vsize as usize)?)),
140            ValueTag::Resolution => Ok(IppValue::Resolution {
141                crossfeed: reader.read_i32::<BigEndian>()?,
142                feed: reader.read_i32::<BigEndian>()?,
143                units: reader.read_i8()?,
144            }),
145            _ => Ok(IppValue::Other {
146                tag: vtag,
147                data: reader.read_bytes(vsize as usize)?,
148            }),
149        }
150    }
151}
152
153impl IppWriter for IppValue {
154    /// Write value to binary stream
155    fn write(&self, writer: &mut Write) -> io::Result<usize> {
156        match *self {
157            IppValue::Integer(i) | IppValue::Enum(i) => {
158                writer.write_u16::<BigEndian>(4)?;
159                writer.write_i32::<BigEndian>(i)?;
160                Ok(6)
161            }
162            IppValue::RangeOfInteger { min, max } => {
163                writer.write_u16::<BigEndian>(8)?;
164                writer.write_i32::<BigEndian>(min)?;
165                writer.write_i32::<BigEndian>(max)?;
166                Ok(10)
167            }
168            IppValue::Boolean(b) => {
169                writer.write_u16::<BigEndian>(1)?;
170                writer.write_u8(if b { 1 } else { 0 })?;
171                Ok(3)
172            }
173            IppValue::Keyword(ref s)
174            | IppValue::OctetString(ref s)
175            | IppValue::TextWithoutLanguage(ref s)
176            | IppValue::NameWithoutLanguage(ref s)
177            | IppValue::Charset(ref s)
178            | IppValue::NaturalLanguage(ref s)
179            | IppValue::Uri(ref s)
180            | IppValue::MimeMediaType(ref s)
181            | IppValue::MemberAttrName(ref s) => {
182                writer.write_u16::<BigEndian>(s.len() as u16)?;
183                writer.write_all(s.as_bytes())?;
184                Ok(2 + s.len())
185            }
186            IppValue::ListOf(ref list) => {
187                let mut retval = 0;
188                for (i, item) in list.iter().enumerate() {
189                    retval += item.write(writer)?;
190                    if i < list.len() - 1 {
191                        writer.write_u8(self.to_tag() as u8)?;
192                        writer.write_u16::<BigEndian>(0)?;
193                        retval += 3;
194                    }
195                }
196                Ok(retval)
197            }
198            IppValue::Collection(ref list) => {
199                let mut retval = 0;
200
201                // begin collection: value size is 0
202                writer.write_u16::<BigEndian>(0)?;
203                retval += 2;
204
205                for item in list.iter() {
206                    // item tag
207                    writer.write_u8(item.to_tag() as u8)?;
208                    // name size is zero, this is a collection
209                    writer.write_u16::<BigEndian>(0)?;
210                    // write the item
211                    retval += 3 + item.write(writer)?;
212                }
213                // write end collection attribute
214                writer.write_u8(ValueTag::EndCollection as u8)?;
215                writer.write_u32::<BigEndian>(0)?;
216                retval += 5;
217
218                Ok(retval)
219            }
220            IppValue::DateTime {
221                year,
222                month,
223                day,
224                hour,
225                minutes,
226                seconds,
227                deciseconds,
228                utcdir,
229                utchours,
230                utcmins,
231            } => {
232                writer.write_u16::<BigEndian>(11)?;
233
234                writer.write_u16::<BigEndian>(year)?;
235                writer.write_u8(month)?;
236                writer.write_u8(day)?;
237                writer.write_u8(hour)?;
238                writer.write_u8(minutes)?;
239                writer.write_u8(seconds)?;
240                writer.write_u8(deciseconds)?;
241                writer.write_u8(utcdir as u8)?;
242                writer.write_u8(utchours)?;
243                writer.write_u8(utcmins)?;
244
245                Ok(13)
246            }
247            IppValue::Resolution { crossfeed, feed, units } => {
248                writer.write_u16::<BigEndian>(9)?;
249                writer.write_i32::<BigEndian>(crossfeed)?;
250                writer.write_i32::<BigEndian>(feed)?;
251                writer.write_i8(units)?;
252                Ok(9)
253            }
254            IppValue::Other { ref data, .. } => {
255                writer.write_u16::<BigEndian>(data.len() as u16)?;
256                writer.write_all(data)?;
257                Ok(2 + data.len())
258            }
259        }
260    }
261}
262
263/// Implement Display trait to print the value
264impl fmt::Display for IppValue {
265    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266        match *self {
267            IppValue::Integer(i) | IppValue::Enum(i) => write!(f, "{}", i),
268            IppValue::RangeOfInteger { min, max } => write!(f, "{}..{}", min, max),
269            IppValue::Boolean(b) => write!(f, "{}", if b { "true" } else { "false" }),
270            IppValue::Keyword(ref s)
271            | IppValue::OctetString(ref s)
272            | IppValue::TextWithoutLanguage(ref s)
273            | IppValue::NameWithoutLanguage(ref s)
274            | IppValue::Charset(ref s)
275            | IppValue::NaturalLanguage(ref s)
276            | IppValue::Uri(ref s)
277            | IppValue::MimeMediaType(ref s)
278            | IppValue::MemberAttrName(ref s) => write!(f, "{}", s),
279            IppValue::ListOf(ref list) => {
280                let s: Vec<String> = list.iter().map(|v| format!("{}", v)).collect();
281                write!(f, "[{}]", s.join(", "))
282            }
283            IppValue::Collection(ref list) => {
284                let s: Vec<String> = list.iter().map(|v| format!("{}", v)).collect();
285                write!(f, "<{}>", s.join(", "))
286            }
287            IppValue::DateTime {
288                year,
289                month,
290                day,
291                hour,
292                minutes,
293                seconds,
294                deciseconds,
295                utcdir,
296                utchours,
297                ..
298            } => write!(
299                f,
300                "{}-{}-{},{}:{}:{}.{},{}{}utc",
301                year, month, day, hour, minutes, seconds, deciseconds, utcdir as char, utchours
302            ),
303            IppValue::Resolution { crossfeed, feed, units } => {
304                write!(f, "{}x{}{}", crossfeed, feed, if units == 3 { "in" } else { "cm" })
305            }
306
307            IppValue::Other { tag, ref data } => write!(f, "{:0x}: {:?}", tag, data),
308        }
309    }
310}
311
312impl<'a> IntoIterator for &'a IppValue {
313    type Item = &'a IppValue;
314    type IntoIter = IppValueIterator<'a>;
315
316    fn into_iter(self) -> Self::IntoIter {
317        IppValueIterator { value: self, index: 0 }
318    }
319}
320
321pub struct IppValueIterator<'a> {
322    value: &'a IppValue,
323    index: usize,
324}
325
326impl<'a> Iterator for IppValueIterator<'a> {
327    type Item = &'a IppValue;
328
329    fn next(&mut self) -> Option<Self::Item> {
330        match self.value {
331            IppValue::ListOf(ref list) | IppValue::Collection(ref list) => {
332                if self.index < list.len() {
333                    self.index += 1;
334                    Some(&list[self.index - 1])
335                } else {
336                    None
337                }
338            }
339            _ => {
340                if self.index == 0 {
341                    self.index += 1;
342                    Some(self.value)
343                } else {
344                    None
345                }
346            }
347        }
348    }
349}
350
351#[cfg(test)]
352mod tests {
353    use crate::{ipp::DelimiterTag, IppAttribute};
354
355    use super::*;
356
357    #[test]
358    fn test_value_iterator_single() {
359        let val = IppValue::Integer(1234);
360
361        for v in &val {
362            assert_eq!(*v, val);
363        }
364    }
365
366    #[test]
367    fn test_value_iterator_multiple() {
368        let list = vec![IppValue::Integer(1234), IppValue::Integer(5678)];
369        let val = IppValue::ListOf(list.clone());
370
371        for v in val.into_iter().enumerate() {
372            assert_eq!(*v.1, list[v.0]);
373        }
374    }
375
376    #[test]
377    fn test_collection_de_serialize() {
378        let attr = IppAttribute::new(
379            "coll",
380            IppValue::Collection(vec![IppValue::Integer(0x11111111), IppValue::Integer(0x22222222)]),
381        );
382        let mut buf = Vec::new();
383        assert!(attr.write(&mut io::Cursor::new(&mut buf)).is_ok());
384
385        assert_eq!(
386            vec![
387                0x34, 0, 4, b'c', b'o', b'l', b'l', 0, 0, 0x21, 0, 0, 0, 4, 0x11, 0x11, 0x11, 0x11, 0x21, 0, 0, 0, 4,
388                0x22, 0x22, 0x22, 0x22, 0x37, 0, 0, 0, 0,
389            ],
390            buf
391        );
392
393        let mut data = vec![1, 1, 0, 0, 0, 0, 0, 0, 4];
394        data.extend(buf);
395        data.extend(vec![3]);
396
397        let result = crate::parser::IppParser::new(&mut io::Cursor::new(data)).parse();
398        assert!(result.is_ok());
399
400        let res = result.ok().unwrap();
401        let attrs = res.attributes.groups_of(DelimiterTag::PrinterAttributes)[0].attributes();
402        let attr = attrs.get("coll").unwrap();
403        assert_eq!(
404            attr.value().as_collection(),
405            Some(&vec![IppValue::Integer(0x11111111), IppValue::Integer(0x22222222)])
406        );
407    }
408}