1use alloc::{borrow::Cow, vec::Vec};
5use core::ops::Range;
6use zencodec::{
7 Orientation, enough,
8 metadata_rewrite::{Action, Compression, Entry, Error, Plan, RewriteRequest, Services},
9};
10mod exif;
11
12#[derive(Clone)]
14pub struct Context<'a> {
15 request: RewriteRequest<'a>,
16}
17impl<'a> core::ops::Deref for Context<'a> {
18 type Target = RewriteRequest<'a>;
19 fn deref(&self) -> &Self::Target {
20 &self.request
21 }
22}
23impl<'a> Context<'a> {
24 pub fn new(request: &RewriteRequest<'a>) -> Result<Self, Error> {
26 let context = Self {
27 request: request.clone(),
28 };
29 context.check()?;
30 if (request.edit_xmp() || request.normalize_icc()) && request.services().is_none() {
31 return Err(Error::Unsupported("metadata service not supplied"));
32 }
33 Ok(context)
34 }
35 pub fn check(&self) -> Result<(), Error> {
37 self.pulse()
38 .unwrap_or(&zencodec::NoPulse)
39 .check()
40 .map_err(|reason| match reason {
41 enough::StopReason::Cancelled => Error::Cancelled,
42 other => Error::Stopped(other),
43 })?;
44 if self.input().len() > self.max_input() {
45 return Err(Error::Limit(zencodec::LimitKind::InputSize));
46 }
47 Ok(())
48 }
49 pub fn packet(&self, size: usize) -> Result<(), Error> {
51 self.check()?;
52 if size > self.max_metadata() {
53 Err(Error::Limit(zencodec::LimitKind::MetadataSize))
54 } else {
55 Ok(())
56 }
57 }
58 pub fn with_input(mut self, input: &'a [u8]) -> Self {
60 self.request = self.request.with_input(input);
61 self
62 }
63 pub fn with_xmp_editing(mut self, enabled: bool) -> Self {
65 self.request = self.request.with_xmp_editing(enabled);
66 self
67 }
68 pub fn services(&self) -> Result<&dyn Services, Error> {
70 self.request
71 .services()
72 .ok_or(Error::Unsupported("metadata service not supplied"))
73 }
74 pub fn xmp(&self, xml: &[u8]) -> Result<Option<Vec<u8>>, Error> {
76 self.packet(xml.len())?;
77 if !self.edit_xmp() {
78 self.check()?;
79 return if self.policy().fields().xmp.keeps() {
80 let mut kept = Vec::new();
81 kept.try_reserve_exact(xml.len())
82 .map_err(|_| Error::Allocation)?;
83 kept.extend_from_slice(xml);
84 Ok(Some(kept))
85 } else {
86 Ok(None)
87 };
88 }
89 let out = self
90 .services()?
91 .rewrite_xmp(xml, self.max_metadata(), self.pulse())?;
92 if let Some(ref bytes) = out {
93 self.packet(bytes.len())?;
94 }
95 self.check()?;
96 Ok(out)
97 }
98 pub fn icc(&self, bytes: &[u8]) -> Result<Option<Vec<u8>>, Error> {
100 self.packet(bytes.len())?;
101 if matches!(self.policy().fields().icc, zencodec::IccRetention::Drop) {
102 return Err(Error::Unsupported(
103 "removing an ICC profile requires explicit color conversion",
104 ));
105 }
106 if !self.normalize_icc() {
107 return Ok(None);
108 }
109 let out = self
110 .services()?
111 .normalize_icc(bytes, self.max_metadata(), self.pulse())?;
112 if let Some(ref bytes) = out {
113 self.packet(bytes.len())?;
114 if bytes.len() < 128 || bytes.get(36..40) != Some(b"acsp") {
115 return Err(Error::Service("normalizer returned an invalid ICC header"));
116 }
117 }
118 self.check()?;
119 Ok(out)
120 }
121 pub fn decompress(&self, kind: Compression, bytes: &[u8]) -> Result<Vec<u8>, Error> {
123 self.packet(bytes.len())?;
124 let out = self
125 .services()?
126 .decompress(kind, bytes, self.max_metadata(), self.pulse())?;
127 self.packet(out.len())?;
128 self.check()?;
129 Ok(out)
130 }
131 pub fn exif(&self, bytes: &[u8]) -> Result<Vec<u8>, Error> {
133 self.packet(bytes.len())?;
134 exif::validate(self, bytes)?;
135 let exif = zencodec::exif::Exif::parse(bytes).ok_or(Error::Malformed("EXIF"))?;
136 let orientation = exif.orientation().unwrap_or(Orientation::Identity);
137 let filtered = crate::exif::filter(&exif, &self.policy().fields().exif);
138 if filtered.orientation().unwrap_or(Orientation::Identity) != orientation {
139 return Err(Error::Unsupported(
140 "removing orientation requires explicit pixel transformation",
141 ));
142 }
143 let output = crate::exif::serialize(&filtered);
144 self.packet(output.len())?;
145 Ok(output)
146 }
147}
148
149#[derive(Debug, Default)]
151pub struct PlanBuilder<'a> {
152 parts: Vec<Cow<'a, [u8]>>,
153 entries: Vec<Entry>,
154 size: usize,
155 rewritten: usize,
156 input: Option<&'a [u8]>,
157}
158impl<'a> PlanBuilder<'a> {
159 pub fn new() -> Self {
161 Self::default()
162 }
163 pub fn record(
165 &mut self,
166 req: &Context<'a>,
167 range: Range<usize>,
168 carrier: &'static str,
169 action: Action,
170 bytes: Option<Cow<'a, [u8]>>,
171 ) -> Result<(), Error> {
172 req.check()?;
173 if range.start > range.end || range.end > req.input().len() {
174 return Err(Error::Malformed("rewrite source range"));
175 }
176 let input = req.input();
177 if self
178 .input
179 .is_some_and(|previous| !core::ptr::eq(previous, input))
180 {
181 return Err(Error::Malformed("mixed rewrite inputs"));
182 }
183 if self.entries.len() >= req.max_carriers() {
184 return Err(Error::Limit(zencodec::LimitKind::MetadataCarriers));
185 }
186 let added = bytes.as_ref().map_or(0, |b| b.len());
187 let size = self
188 .size
189 .checked_add(added)
190 .filter(|n| *n <= req.max_output())
191 .ok_or(Error::Limit(zencodec::LimitKind::OutputSize))?;
192 let rewritten = self
193 .rewritten
194 .checked_add(
195 bytes
196 .as_ref()
197 .filter(|b| matches!(b, Cow::Owned(_)))
198 .map_or(0, |b| b.len()),
199 )
200 .ok_or(Error::Limit(zencodec::LimitKind::MetadataRewriteSize))?;
201 if rewritten > req.max_metadata() {
202 return Err(Error::Limit(zencodec::LimitKind::MetadataRewriteSize));
203 }
204 self.entries.try_reserve(1).map_err(|_| Error::Allocation)?;
205 if bytes.is_some() {
206 self.parts.try_reserve(1).map_err(|_| Error::Allocation)?;
207 }
208 self.entries.push(Entry::new(range, carrier, action));
209 if let Some(bytes) = bytes {
210 self.parts.push(bytes);
211 }
212 self.size = size;
213 self.rewritten = rewritten;
214 self.input = Some(input);
215 Ok(())
216 }
217
218 pub fn keep(
220 &mut self,
221 req: &Context<'a>,
222 range: Range<usize>,
223 name: &'static str,
224 ) -> Result<(), Error> {
225 let bytes = req
226 .input()
227 .get(range.clone())
228 .ok_or(Error::Malformed("rewrite source range"))?;
229 self.record(req, range, name, Action::Keep, Some(Cow::Borrowed(bytes)))
230 }
231 pub fn remove(
233 &mut self,
234 req: &Context<'a>,
235 range: Range<usize>,
236 name: &'static str,
237 ) -> Result<(), Error> {
238 self.record(req, range, name, Action::Remove, None)
239 }
240 pub fn output_len(&self) -> usize {
242 self.size
243 }
244 pub fn inject_after_first(
246 &mut self,
247 req: &Context<'a>,
248 bytes: Vec<u8>,
249 name: &'static str,
250 ) -> Result<(), Error> {
251 if self.parts.is_empty() {
252 return Err(Error::Malformed("missing leading carrier"));
253 }
254 self.record(req, 0..0, name, Action::Rewrite, Some(Cow::Owned(bytes)))?;
255 let part = self
256 .parts
257 .pop()
258 .ok_or(Error::Malformed("missing generated carrier"))?;
259 self.parts.insert(1, part);
260 Ok(())
261 }
262 pub fn remove_kept_range(
264 &mut self,
265 req: &Context<'a>,
266 range: &Range<usize>,
267 ) -> Result<(), Error> {
268 req.check()?;
269 let input = req.input();
270 if self
271 .input
272 .is_some_and(|previous| !core::ptr::eq(previous, input))
273 {
274 return Err(Error::Malformed("mixed rewrite inputs"));
275 }
276 let source = req
277 .input()
278 .get(range.clone())
279 .ok_or(Error::Malformed("rewrite source range"))?;
280 let index = self.parts.iter().position(|p| matches!(p, Cow::Borrowed(b) if b.as_ptr() == source.as_ptr() && b.len() == source.len()))
281 .ok_or(Error::Malformed("missing retained carrier"))?;
282 self.size -= self.parts.remove(index).len();
283 for entry in &mut self.entries {
284 if entry.source_range() == *range {
285 *entry = Entry::new(range.clone(), entry.carrier(), Action::Remove);
286 }
287 }
288 Ok(())
289 }
290 pub fn append(&mut self, req: &Context<'a>, other: Self, offset: usize) -> Result<(), Error> {
292 req.check()?;
293 let input = req.input();
294 if self
295 .input
296 .is_some_and(|previous| !core::ptr::eq(previous, input))
297 {
298 return Err(Error::Malformed("mixed rewrite inputs"));
299 }
300 if offset
301 .checked_add(other.input.map_or(0, <[u8]>::len))
302 .is_none_or(|end| end > req.input().len())
303 {
304 return Err(Error::Malformed("contained input range"));
305 }
306 if let Some(child_input) = other.input {
307 let expected = &input[offset..offset + child_input.len()];
310 if !core::ptr::eq(child_input, expected) {
311 return Err(Error::Malformed("contained input identity"));
312 }
313 }
314 let size = self
315 .size
316 .checked_add(other.size)
317 .filter(|n| *n <= req.max_output())
318 .ok_or(Error::Limit(zencodec::LimitKind::OutputSize))?;
319 let rewritten = self
320 .rewritten
321 .checked_add(other.rewritten)
322 .ok_or(Error::Limit(zencodec::LimitKind::MetadataRewriteSize))?;
323 if rewritten > req.max_metadata() {
324 return Err(Error::Limit(zencodec::LimitKind::MetadataRewriteSize));
325 }
326 if self
327 .entries
328 .len()
329 .checked_add(other.entries.len())
330 .is_none_or(|n| n > req.max_carriers())
331 {
332 return Err(Error::Limit(zencodec::LimitKind::MetadataCarriers));
333 }
334 for entry in &other.entries {
335 let range = entry.source_range();
336 if range.start.checked_add(offset).is_none()
337 || range
338 .end
339 .checked_add(offset)
340 .is_none_or(|end| end > req.input().len())
341 {
342 return Err(Error::Malformed("report offset"));
343 }
344 }
345 self.parts
346 .try_reserve(other.parts.len())
347 .map_err(|_| Error::Allocation)?;
348 self.entries
349 .try_reserve(other.entries.len())
350 .map_err(|_| Error::Allocation)?;
351 for entry in other.entries {
352 let range = entry.source_range();
353 self.entries.push(Entry::new(
354 range.start + offset..range.end + offset,
355 entry.carrier(),
356 entry.action(),
357 ));
358 }
359 self.input = Some(input);
360 self.parts.extend(other.parts);
361 self.size = size;
362 self.rewritten = rewritten;
363 Ok(())
364 }
365 pub fn finish(self) -> Result<Plan<'a>, Error> {
367 let plan = Plan::from_parts(self.input.map_or(0, <[u8]>::len), self.parts, self.entries)?;
368 if plan.output_len() != self.size {
369 return Err(Error::Malformed("rewrite length accounting"));
370 }
371 Ok(plan)
372 }
373}