1 use crate::types::ExifError;
2 
3 use std::fmt::{self, Display};
4 
5 #[derive(Copy, Clone, Eq, PartialEq)]
6 pub enum FileType {
7     Unknown,
8     JPEG,
9     TIFF,
10 }
11 
12 impl Display for FileType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result13     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14         f.write_str(self.as_str())
15     }
16 }
17 
18 impl FileType {
as_str(&self) -> &'static str19     pub fn as_str(&self) -> &'static str {
20         match self {
21             Self::Unknown => "application/octet-stream",
22             Self::JPEG => "image/jpeg",
23             Self::TIFF => "image/tiff",
24         }
25     }
26 }
27 
28 /// Detect the type of an image contained in a byte buffer
detect_type(contents: &[u8]) -> FileType29 pub(crate) fn detect_type(contents: &[u8]) -> FileType {
30     if contents.len() < 11 {
31         return FileType::Unknown;
32     }
33 
34     if contents[0] == 0xff && contents[1] == 0xd8 && contents[2] == 0xff && // contents[3] == 0xe0 &&
35         contents[6] == b'J' && contents[7] == b'F' && contents[8] == b'I' && contents[9] == b'F' &&
36             contents[10] == 0
37     {
38         return FileType::JPEG;
39     }
40     if contents[0] == 0xff && contents[1] == 0xd8 && contents[2] == 0xff && // contents[3] == 0xe0
41         contents[6] == b'E' && contents[7] == b'x' && contents[8] == b'i' && contents[9] == b'f' &&
42             contents[10] == 0
43     {
44         return FileType::JPEG;
45     }
46     if contents[0] == b'I' && contents[1] == b'I' && contents[2] == 42 && contents[3] == 0 {
47         /* TIFF little-endian */
48         return FileType::TIFF;
49     }
50     if contents[0] == b'M' && contents[1] == b'M' && contents[2] == 0 && contents[3] == 42 {
51         /* TIFF big-endian */
52         return FileType::TIFF;
53     }
54     FileType::Unknown
55 }
56 
57 /// Find the embedded TIFF in a JPEG image (that in turn contains the EXIF data)
find_embedded_tiff_in_jpeg(contents: &[u8]) -> Result<(usize, usize), ExifError>58 pub fn find_embedded_tiff_in_jpeg(contents: &[u8]) -> Result<(usize, usize), ExifError> {
59     let mut offset = 2 as usize;
60 
61     while offset < contents.len() {
62         if contents.len() < (offset + 4) {
63             return Err(ExifError::JpegWithoutExif(
64                 "JPEG truncated in marker header".to_string(),
65             ));
66         }
67 
68         let marker: u16 = u16::from(contents[offset]) * 256 + u16::from(contents[offset + 1]);
69 
70         if marker < 0xff00 {
71             return Err(ExifError::JpegWithoutExif(format!(
72                 "Invalid marker {:x}",
73                 marker
74             )));
75         }
76 
77         offset += 2;
78         let size = (contents[offset] as usize) * 256 + (contents[offset + 1] as usize);
79 
80         if size < 2 {
81             return Err(ExifError::JpegWithoutExif(
82                 "JPEG marker size must be at least 2 (because of the size word)".to_string(),
83             ));
84         }
85         if contents.len() < (offset + size) {
86             return Err(ExifError::JpegWithoutExif(
87                 "JPEG truncated in marker body".to_string(),
88             ));
89         }
90 
91         if marker == 0xffe1 {
92             if size < 8 {
93                 return Err(ExifError::JpegWithoutExif(
94                     "EXIF preamble truncated".to_string(),
95                 ));
96             }
97 
98             if contents[offset + 2..offset + 8] != [b'E', b'x', b'i', b'f', 0, 0] {
99                 return Err(ExifError::JpegWithoutExif(
100                     "EXIF preamble unrecognized".to_string(),
101                 ));
102             }
103 
104             // The offset and size of the block, excluding size and 'Exif\0\0'.
105             return Ok((offset + 8, size - 8));
106         }
107         if marker == 0xffda {
108             // last marker
109             return Err(ExifError::JpegWithoutExif(
110                 "Last mark found and no EXIF".to_string(),
111             ));
112         }
113         offset += size;
114     }
115 
116     Err(ExifError::JpegWithoutExif(
117         "Scan past EOF and no EXIF found".to_string(),
118     ))
119 }
120