// // Copyright (c) 2016 KAMADA Ken'ichi. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. // use std::io::{BufRead, ErrorKind}; use crate::error::Error; use crate::util::{read8, read16}; mod marker { // The first byte of a marker. pub const P: u8 = 0xff; // Marker codes. pub const Z: u8 = 0x00; // Not a marker but a byte stuffing. pub const TEM: u8 = 0x01; pub const RST0: u8 = 0xd0; pub const RST7: u8 = 0xd7; pub const SOI: u8 = 0xd8; pub const EOI: u8 = 0xd9; pub const SOS: u8 = 0xda; pub const APP1: u8 = 0xe1; } // SOI marker as the JPEG header. const JPEG_SIG: [u8; 2] = [marker::P, marker::SOI]; // Exif identifier code "Exif\0\0". [EXIF23 4.7.2] const EXIF_ID: [u8; 6] = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00]; /// Get the Exif attribute information segment from a JPEG file. pub fn get_exif_attr(reader: &mut R) -> Result, Error> where R: BufRead { match get_exif_attr_sub(reader) { Err(Error::Io(ref e)) if e.kind() == ErrorKind::UnexpectedEof => Err(Error::InvalidFormat("Broken JPEG file")), r => r, } } fn get_exif_attr_sub(reader: &mut R) -> Result, Error> where R: BufRead { let mut soi = [0u8; 2]; reader.read_exact(&mut soi)?; if soi != [marker::P, marker::SOI] { return Err(Error::InvalidFormat("Not a JPEG file")); } loop { // Find a marker prefix. Discard non-ff bytes, which appear if // we are in the scan data after SOS or we are out of sync. reader.read_until(marker::P, &mut Vec::new())?; // Get a marker code. let mut code; loop { code = read8(reader)?; if code != marker::P { break; } } // Continue or return early on stand-alone markers. match code { marker::Z | marker::TEM | marker::RST0..=marker::RST7 => continue, marker::SOI => return Err(Error::InvalidFormat("Unexpected SOI")), marker::EOI => return Err(Error::NotFound("JPEG")), _ => {}, } // Read marker segments. let len = read16(reader)?.checked_sub(2) .ok_or(Error::InvalidFormat("Invalid segment length"))?; let mut seg = vec![0; len.into()]; reader.read_exact(&mut seg)?; if code == marker::APP1 && seg.starts_with(&EXIF_ID) { seg.drain(..EXIF_ID.len()); return Ok(seg); } if code == marker::SOS { // Skipping the scan data is handled in the main loop, // so there is nothing to do here. } } } pub fn is_jpeg(buf: &[u8]) -> bool { buf.starts_with(&JPEG_SIG) } #[cfg(test)] mod tests { use super::*; #[test] fn truncated() { let sets: &[&[u8]] = &[ b"", b"\xff", b"\xff\xd8", b"\xff\xd8\x00", b"\xff\xd8\xff", b"\xff\xd8\xff\xe1\x00\x08\x03\x04", ]; for &data in sets { assert_err_pat!(get_exif_attr(&mut &data[..]), Error::InvalidFormat("Broken JPEG file")); } let mut data = b"\xff\xd8\xff\xe1\x00\x08Exif\0\0".to_vec(); assert_eq!(get_exif_attr(&mut &data[..]).unwrap(), b""); while let Some(_) = data.pop() { get_exif_attr(&mut &data[..]).unwrap_err(); } } #[test] fn no_exif() { let data = b"\xff\xd8\xff\xd9"; assert_err_pat!(get_exif_attr(&mut &data[..]), Error::NotFound(_)); } #[test] fn out_of_sync() { let data = b"\xff\xd8\x01\x02\x03\xff\x00\xff\xd9"; assert_err_pat!(get_exif_attr(&mut &data[..]), Error::NotFound(_)); } #[test] fn empty() { let data = b"\xff\xd8\xff\xe1\x00\x08Exif\0\0\xff\xd9"; assert_ok!(get_exif_attr(&mut &data[..]), []); } #[test] fn non_empty() { let data = b"\xff\xd8\xff\xe1\x00\x0aExif\0\0\xbe\xad\xff\xd9"; assert_ok!(get_exif_attr(&mut &data[..]), [0xbe, 0xad]); } }