1use std::io::{BufRead, ErrorKind};
28
29use crate::error::Error;
30use crate::util::{read8, read16};
31
32mod marker {
33 pub const P: u8 = 0xff;
35 pub const Z: u8 = 0x00; pub const TEM: u8 = 0x01;
38 pub const RST0: u8 = 0xd0;
39 pub const RST7: u8 = 0xd7;
40 pub const SOI: u8 = 0xd8;
41 pub const EOI: u8 = 0xd9;
42 pub const SOS: u8 = 0xda;
43 pub const APP1: u8 = 0xe1;
44}
45
46const JPEG_SIG: [u8; 2] = [marker::P, marker::SOI];
48
49const EXIF_ID: [u8; 6] = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00];
51
52pub fn get_exif_attr<R>(reader: &mut R)
54 -> Result<Vec<u8>, Error> where R: BufRead {
55 match get_exif_attr_sub(reader) {
56 Err(Error::Io(ref e)) if e.kind() == ErrorKind::UnexpectedEof =>
57 Err(Error::InvalidFormat("Broken JPEG file")),
58 r => r,
59 }
60}
61
62fn get_exif_attr_sub<R>(reader: &mut R)
63 -> Result<Vec<u8>, Error> where R: BufRead {
64 let mut soi = [0u8; 2];
65 reader.read_exact(&mut soi)?;
66 if soi != [marker::P, marker::SOI] {
67 return Err(Error::InvalidFormat("Not a JPEG file"));
68 }
69 loop {
70 reader.read_until(marker::P, &mut Vec::new())?;
73 let mut code;
75 loop {
76 code = read8(reader)?;
77 if code != marker::P { break; }
78 }
79 match code {
81 marker::Z | marker::TEM | marker::RST0..=marker::RST7 => continue,
82 marker::SOI => return Err(Error::InvalidFormat("Unexpected SOI")),
83 marker::EOI => return Err(Error::NotFound("JPEG")),
84 _ => {},
85 }
86 let len = read16(reader)?.checked_sub(2)
88 .ok_or(Error::InvalidFormat("Invalid segment length"))?;
89 let mut seg = vec![0; len.into()];
90 reader.read_exact(&mut seg)?;
91 if code == marker::APP1 && seg.starts_with(&EXIF_ID) {
92 seg.drain(..EXIF_ID.len());
93 return Ok(seg);
94 }
95 if code == marker::SOS {
96 }
99 }
100}
101
102pub fn is_jpeg(buf: &[u8]) -> bool {
103 buf.starts_with(&JPEG_SIG)
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn truncated() {
112 let sets: &[&[u8]] = &[
113 b"",
114 b"\xff",
115 b"\xff\xd8",
116 b"\xff\xd8\x00",
117 b"\xff\xd8\xff",
118 b"\xff\xd8\xff\xe1\x00\x08\x03\x04",
119 ];
120 for &data in sets {
121 assert_err_pat!(get_exif_attr(&mut &data[..]),
122 Error::InvalidFormat("Broken JPEG file"));
123 }
124
125 let mut data = b"\xff\xd8\xff\xe1\x00\x08Exif\0\0".to_vec();
126 assert_eq!(get_exif_attr(&mut &data[..]).unwrap(), b"");
127 while let Some(_) = data.pop() {
128 get_exif_attr(&mut &data[..]).unwrap_err();
129 }
130 }
131
132 #[test]
133 fn no_exif() {
134 let data = b"\xff\xd8\xff\xd9";
135 assert_err_pat!(get_exif_attr(&mut &data[..]),
136 Error::NotFound(_));
137 }
138
139 #[test]
140 fn out_of_sync() {
141 let data = b"\xff\xd8\x01\x02\x03\xff\x00\xff\xd9";
142 assert_err_pat!(get_exif_attr(&mut &data[..]),
143 Error::NotFound(_));
144 }
145
146 #[test]
147 fn empty() {
148 let data = b"\xff\xd8\xff\xe1\x00\x08Exif\0\0\xff\xd9";
149 assert_ok!(get_exif_attr(&mut &data[..]), []);
150 }
151
152 #[test]
153 fn non_empty() {
154 let data = b"\xff\xd8\xff\xe1\x00\x0aExif\0\0\xbe\xad\xff\xd9";
155 assert_ok!(get_exif_attr(&mut &data[..]), [0xbe, 0xad]);
156 }
157}