reader.rs (9121B)
1 // Copyright 2024 Adobe. All rights reserved. 2 // This file is licensed to you under the Apache License, 3 // Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) 4 // or the MIT license (http://opensource.org/licenses/MIT), 5 // at your option. 6 7 // Unless required by applicable law or agreed to in writing, 8 // this software is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or 10 // implied. See the LICENSE-MIT and LICENSE-APACHE files for the 11 // specific language governing permissions and limitations under 12 // each license. 13 14 //! The Reader provides a way to read a manifest store from an asset. 15 //! It also performs validation on the manifest store. 16 17 #[cfg(feature = "file_io")] 18 use std::fs::{read, File}; 19 use std::io::{Read, Seek, Write}; 20 21 use async_generic::async_generic; 22 23 #[cfg(feature = "file_io")] 24 use crate::error::Error; 25 use crate::{ 26 claim::ClaimAssetData, error::Result, manifest_store::ManifestStore, 27 settings::get_settings_value, status_tracker::DetailedStatusTracker, store::Store, 28 validation_status::ValidationStatus, Manifest, ManifestStoreReport, 29 }; 30 31 /// A reader for the manifest store. 32 pub struct Reader { 33 pub(crate) manifest_store: ManifestStore, 34 } 35 36 impl Reader { 37 /// Create a manifest store Reader from a stream. 38 /// # Arguments 39 /// * `format` - The format of the stream. 40 /// * `stream` - The stream to read from. 41 /// # Returns 42 /// A reader for the manifest store. 43 /// # Errors 44 /// If the stream is not a valid manifest store. 45 /// validation status should be checked for non severe errors 46 /// # Example 47 /// ```no_run 48 /// use std::io::Cursor; 49 /// 50 /// use c2pa::Reader; 51 /// let mut stream = Cursor::new(include_bytes!("../tests/fixtures/CA.jpg")); 52 /// let reader = Reader::from_stream("image/jpeg", stream).unwrap(); 53 /// println!("{}", reader.json()); 54 /// ``` 55 #[async_generic()] 56 pub fn from_stream(format: &str, mut stream: impl Read + Seek + Send) -> Result<Reader> { 57 let verify = get_settings_value::<bool>("verify.verify_after_reading")?; // defaults to true 58 let reader = if _sync { 59 ManifestStore::from_stream(format, &mut stream, verify) 60 } else { 61 ManifestStore::from_stream_async(format, &mut stream, verify).await 62 }?; 63 Ok(Reader { 64 manifest_store: reader, 65 }) 66 } 67 68 #[cfg(feature = "file_io")] 69 /// Create a manifest store Reader from a file. 70 /// # Arguments 71 /// * `path` - The path to the file. 72 /// # Returns 73 /// A reader for the manifest store. 74 /// # Errors 75 /// If the file is not a valid manifest store. 76 /// validation status should be checked for non severe errors. 77 /// # Example 78 /// ```no_run 79 /// use c2pa::Reader; 80 /// let reader = Reader::from_file("path/to/file.jpg").unwrap(); 81 /// ``` 82 /// # Note 83 /// If the file does not have a manifest store, the function will check for a sidecar manifest 84 /// with the same name and a .c2pa extension. 85 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Reader> { 86 let path = path.as_ref(); 87 let format = crate::format_from_path(path).ok_or(crate::Error::UnsupportedType)?; 88 let mut file = File::open(path)?; 89 let result = Self::from_stream(&format, &mut file); 90 if let Err(Error::JumbfNotFound) = result { 91 // if not embedded or cloud, check for sidecar first and load if it exists 92 let potential_sidecar_path = path.with_extension("c2pa"); 93 if potential_sidecar_path.exists() { 94 let manifest_data = read(potential_sidecar_path)?; 95 return Self::from_manifest_data_and_stream(&manifest_data, &format, &mut file); 96 } 97 } 98 result 99 } 100 101 /// Create a manifest store [`Reader`]` from a JSON string. 102 /// # Arguments 103 /// * `json` - A Json String containing a manifest store definition. 104 /// # Returns 105 /// A [`Reader`]` for the manifest store. 106 /// # Note 107 /// This should only be used for testing 108 /// Any referenced resources will not be available 109 pub fn from_json(json: &str) -> Result<Reader> { 110 let manifest_store = serde_json::from_str(json)?; 111 Ok(Reader { manifest_store }) 112 } 113 114 /// Create a manifest store [`Reader`] from existing c2pa_data and a stream 115 /// You can use this to validate a remote manifest or a sidecar manifest 116 /// # Arguments 117 /// * `c2pa_data` - The c2pa data (a manifest store in JUMBF format) 118 /// * `format` - The format of the stream 119 /// * `stream` - The stream to verify the store against 120 /// # Returns 121 /// A [`Reader`] for the manifest store 122 /// # Errors 123 /// If the c2pa_data is not valid, or severe errors occur in validation 124 /// validation status should be checked for non severe errors 125 #[async_generic()] 126 pub fn from_manifest_data_and_stream( 127 c2pa_data: &[u8], 128 format: &str, 129 mut stream: impl Read + Seek + Send, 130 ) -> Result<Reader> { 131 let mut validation_log = DetailedStatusTracker::new(); 132 133 // first we convert the JUMBF into a usable store 134 let store = Store::from_jumbf(c2pa_data, &mut validation_log)?; 135 136 if _sync { 137 Store::verify_store( 138 &store, 139 &mut ClaimAssetData::Stream(&mut stream, format), 140 &mut validation_log, 141 )?; 142 } else { 143 Store::verify_store_async( 144 &store, 145 &mut ClaimAssetData::Stream(&mut stream, format), 146 &mut validation_log, 147 ) 148 .await?; 149 } 150 151 Ok(Reader { 152 manifest_store: ManifestStore::from_store(store, &validation_log), 153 }) 154 } 155 156 /// Get the manifest store as a JSON string 157 pub fn json(&self) -> String { 158 self.manifest_store.to_string() 159 } 160 161 /// Get the [`ValidationStatus`] array of the manifest store if it exists. 162 /// 163 /// This validation report only includes error statuses on applied to the active manifest. 164 /// And error statuses for ingredients that are not already reported by the ingredient status. 165 /// The uri field can be used to identify the associated manifest. 166 /// # Example 167 /// ```no_run 168 /// use c2pa::Reader; 169 /// let stream = std::io::Cursor::new(include_bytes!("../tests/fixtures/CA.jpg")); 170 /// let reader = Reader::from_stream("image/jpeg", stream).unwrap(); 171 /// let status = reader.validation_status(); 172 /// ``` 173 /// # Note 174 /// The validation status should be checked for validation errors. 175 pub fn validation_status(&self) -> Option<&[ValidationStatus]> { 176 self.manifest_store.validation_status() 177 } 178 179 /// Return the active [`Manifest`] if it exists. 180 pub fn active_manifest(&self) -> Option<&Manifest> { 181 self.manifest_store.get_active() 182 } 183 184 /// Return the active [`Manifest`] label if one exists. 185 pub fn active_label(&self) -> Option<&str> { 186 self.manifest_store.active_label() 187 } 188 189 /// Returns an iterator over [`Manifest`][Manifest]s. 190 pub fn iter_manifests(&self) -> impl Iterator<Item = &Manifest> + '_ { 191 self.manifest_store.manifests().values() 192 } 193 194 /// Return a [`Manifest`] for a given label if it exists. 195 /// # Arguments 196 /// * `label` - The label of the requested [`Manifest`] 197 pub fn get_manifest(&self, label: &str) -> Option<&Manifest> { 198 self.manifest_store.get(label) 199 } 200 201 /// Write a resource identified by URI to the given stream. 202 /// # Arguments 203 /// * `uri` - The URI of the resource to write (from an identifier field). 204 /// * `stream` - The stream to write to. 205 /// # Returns 206 /// The number of bytes written. 207 /// # Errors 208 /// If the resource does not exist. 209 /// # Example 210 /// ```no_run 211 /// use c2pa::Reader; 212 /// let stream = std::io::Cursor::new(Vec::new()); 213 /// let reader = Reader::from_file("path/to/file.jpg").unwrap(); 214 /// let manifest = reader.active_manifest().unwrap(); 215 /// let uri = &manifest.thumbnail_ref().unwrap().identifier; 216 /// let bytes_written = reader.resource_to_stream(uri, stream).unwrap(); 217 /// ``` 218 pub fn resource_to_stream( 219 &self, 220 uri: &str, 221 mut stream: impl Write + Read + Seek + Send, 222 ) -> Result<usize> { 223 self.manifest_store 224 .get_resource(uri, &mut stream) 225 .map(|size| size as usize) 226 } 227 } 228 229 impl Default for Reader { 230 fn default() -> Self { 231 Self { 232 manifest_store: ManifestStore::new(), 233 } 234 } 235 } 236 237 impl std::fmt::Display for Reader { 238 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 239 f.write_str(self.json().as_str()) 240 } 241 } 242 243 impl std::fmt::Debug for Reader { 244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 245 let report = ManifestStoreReport::from_store(self.manifest_store.store()) 246 .map_err(|_| std::fmt::Error)?; 247 f.write_str(&report.to_string()) 248 } 249 }