1 //
2 // Copyright (c) 2020 KAMADA Ken'ichi.
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 //    notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 //    notice, this list of conditions and the following disclaimer in the
12 //    documentation and/or other materials provided with the distribution.
13 //
14 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 // SUCH DAMAGE.
25 //
26 
27 use std::io::{BufRead, ErrorKind};
28 
29 use crate::endian::{Endian, BigEndian};
30 use crate::error::Error;
31 use crate::util::{BufReadExt as _, ReadExt as _};
32 
33 // PNG file signature [PNG12 12.12].
34 const PNG_SIG: [u8; 8] = *b"\x89PNG\x0d\x0a\x1a\x0a";
35 // The four-byte chunk type for Exif data.
36 const EXIF_CHUNK_TYPE: [u8; 4] = *b"eXIf";
37 
38 // Get the contents of the eXIf chunk from a PNG file.
get_exif_attr<R>(reader: &mut R) -> Result<Vec<u8>, Error> where R: BufRead39 pub fn get_exif_attr<R>(reader: &mut R)
40                         -> Result<Vec<u8>, Error> where R: BufRead {
41     match get_exif_attr_sub(reader) {
42         Err(Error::Io(ref e)) if e.kind() == ErrorKind::UnexpectedEof =>
43             Err(Error::InvalidFormat("Broken PNG file")),
44         r => r,
45     }
46 }
47 
48 // The location of the eXIf chunk is restricted [PNGEXT150 3.7], but this
49 // reader is liberal about it.
get_exif_attr_sub<R>(reader: &mut R) -> Result<Vec<u8>, Error> where R: BufRead50 fn get_exif_attr_sub<R>(reader: &mut R)
51                         -> Result<Vec<u8>, Error> where R: BufRead {
52     let mut sig = [0u8; 8];
53     reader.read_exact(&mut sig)?;
54     if sig != PNG_SIG {
55         return Err(Error::InvalidFormat("Not a PNG file"));
56     }
57     // Scan the series of chunks.
58     loop {
59         if reader.is_eof()? {
60             return Err(Error::NotFound("PNG"));
61         }
62         let mut lenbuf = [0; 4];
63         reader.read_exact(&mut lenbuf)?;
64         let len = BigEndian::loadu32(&lenbuf, 0) as usize;
65         let mut ctype = [0u8; 4];
66         reader.read_exact(&mut ctype)?;
67         if ctype == EXIF_CHUNK_TYPE {
68             let mut data = Vec::new();
69             reader.read_exact_len(&mut data, len)?;
70             return Ok(data);
71         }
72         // Chunk data and CRC.
73         reader.discard_exact(len.checked_add(4).ok_or(
74             Error::InvalidFormat("Invalid chunk length"))?)?;
75     }
76 }
77 
is_png(buf: &[u8]) -> bool78 pub fn is_png(buf: &[u8]) -> bool {
79     buf.starts_with(&PNG_SIG)
80 }
81 
82 #[cfg(test)]
83 mod tests {
84     use super::*;
85 
86     #[test]
truncated()87     fn truncated() {
88         let sets: &[&[u8]] = &[
89             b"",
90             b"\x89",
91             b"\x89PNG\x0d\x0a\x1a",
92         ];
93         for &data in sets {
94             assert_err_pat!(get_exif_attr(&mut &data[..]),
95                             Error::InvalidFormat("Broken PNG file"));
96         }
97 
98         let mut data = b"\x89PNG\x0d\x0a\x1a\x0a\0\0\0\x04eXIfExif".to_vec();
99         assert_eq!(get_exif_attr(&mut &data[..]).unwrap(), b"Exif");
100         while let Some(_) = data.pop() {
101             get_exif_attr(&mut &data[..]).unwrap_err();
102         }
103     }
104 
105     #[test]
no_exif()106     fn no_exif() {
107         let data = b"\x89PNG\x0d\x0a\x1a\x0a";
108         assert_err_pat!(get_exif_attr(&mut &data[..]),
109                         Error::NotFound(_));
110     }
111 
112     #[test]
empty()113     fn empty() {
114         let data = b"\x89PNG\x0d\x0a\x1a\x0a\0\0\0\0eXIfCRC_";
115         assert_ok!(get_exif_attr(&mut &data[..]), []);
116     }
117 
118     #[test]
non_empty()119     fn non_empty() {
120         let data = b"\x89PNG\x0d\x0a\x1a\x0a\0\0\0\x02eXIf\xbe\xadCRC_";
121         assert_ok!(get_exif_attr(&mut &data[..]), [0xbe, 0xad]);
122     }
123 }
124