1use crate::{CLIP_DOCX, CLIP_HTML, CLIP_RTF, CLIP_TEXT};
6use alloc::vec::Vec;
7
8pub const CLIP_RICH_MAX: usize = 64 << 20;
10
11pub fn clip_many_valid(entries: &[(u32, &[u8])]) -> bool {
14 if entries.is_empty() || entries.len() > 4 {
15 return false;
16 }
17 let mut total = 0usize;
18 for (i, &(fmt, data)) in entries.iter().enumerate() {
19 if !matches!(fmt, CLIP_TEXT | CLIP_HTML | CLIP_RTF | CLIP_DOCX)
20 || entries[..i].iter().any(|e| e.0 == fmt)
21 {
22 return false;
23 }
24 let Some(n) = total.checked_add(data.len()) else {
25 return false;
26 };
27 total = n;
28 if total > CLIP_RICH_MAX {
29 return false;
30 }
31 if matches!(fmt, CLIP_TEXT | CLIP_HTML)
32 && (data.contains(&0) || core::str::from_utf8(data).is_err())
33 {
34 return false;
35 }
36 }
37 true
38}
39
40pub fn clip_html_encode(fragment: &[u8]) -> Option<Vec<u8>> {
42 if fragment.len() > CLIP_RICH_MAX
43 || fragment.contains(&0)
44 || core::str::from_utf8(fragment).is_err()
45 {
46 return None;
47 }
48 let prefix = b"<html><body><!--StartFragment-->";
49 let suffix = b"<!--EndFragment--></body></html>";
50 let header = b"Version:1.0\r\nStartHTML:0000000000\r\nEndHTML:0000000000\r\nStartFragment:0000000000\r\nEndFragment:0000000000\r\n";
51 let start = header.len();
52 let from = start + prefix.len();
53 let to = from + fragment.len();
54 let end = to + suffix.len();
55 let mut data = alloc::format!("Version:1.0\r\nStartHTML:{start:010}\r\nEndHTML:{end:010}\r\nStartFragment:{from:010}\r\nEndFragment:{to:010}\r\n").into_bytes();
56 data.extend_from_slice(prefix);
57 data.extend_from_slice(fragment);
58 data.extend_from_slice(suffix);
59 data.push(0);
60 Some(data)
61}
62
63pub fn clip_html_decode(data: &[u8]) -> Option<Vec<u8>> {
65 if data.len() > CLIP_RICH_MAX + 65536 {
66 return None;
67 }
68 let mut from = None;
69 let mut to = None;
70 let mut header_end = 0;
71 for line in data[..data.len().min(65536)].split_inclusive(|&b| b == b'\n' || b == b'\r') {
72 if line.starts_with(b"<") {
73 break;
74 }
75 header_end += line.len();
76 for (name, target) in [
77 (b"StartFragment:".as_slice(), &mut from),
78 (b"EndFragment:".as_slice(), &mut to),
79 ] {
80 if let Some(value) = line.strip_prefix(name) {
81 if target.is_some() {
82 return None;
83 }
84 let text = core::str::from_utf8(value).ok()?.trim();
85 if text.is_empty() || !text.bytes().all(|b| b.is_ascii_digit()) {
86 return None;
87 }
88 *target = Some(text.parse::<usize>().ok()?);
89 }
90 }
91 if from.is_some() && to.is_some() {
92 break;
93 }
94 }
95 let (from, to) = (from?, to?);
96 if from < header_end || from > to || to > data.len() {
97 return None;
98 }
99 let fragment = &data[from..to];
100 if fragment.len() > CLIP_RICH_MAX || fragment.contains(&0) || core::str::from_utf8(fragment).is_err() {
101 return None;
102 }
103 Some(fragment.to_vec())
104}