lofty/id3/v2/frame/
read.rs

1use super::Frame;
2use super::header::parse::{parse_header, parse_v2_header};
3use crate::config::{ParseOptions, ParsingMode};
4use crate::error::{Id3v2Error, Id3v2ErrorKind, Result};
5use crate::id3::v2::frame::content::parse_content;
6use crate::id3::v2::header::Id3v2Version;
7use crate::id3::v2::tag::ATTACHED_PICTURE_ID;
8use crate::id3::v2::util::synchsafe::{SynchsafeInteger, UnsynchronizedStream};
9use crate::id3::v2::{BinaryFrame, FrameFlags, FrameHeader, FrameId};
10use crate::macros::try_vec;
11
12use std::io::Read;
13
14use byteorder::{BigEndian, ReadBytesExt};
15
16pub(crate) enum ParsedFrame<'a> {
17	Next(Frame<'a>),
18	Skip,
19	Eof,
20}
21
22impl ParsedFrame<'_> {
23	pub(crate) fn read<R>(
24		reader: &mut R,
25		version: Id3v2Version,
26		parse_options: ParseOptions,
27	) -> Result<Self>
28	where
29		R: Read,
30	{
31		let mut size = 0u32;
32
33		// The header will be upgraded to ID3v2.4 past this point, so they can all be treated the same
34		let parse_header_result = match version {
35			Id3v2Version::V2 => parse_v2_header(reader, &mut size),
36			Id3v2Version::V3 => parse_header(reader, &mut size, false, parse_options),
37			Id3v2Version::V4 => parse_header(reader, &mut size, true, parse_options),
38		};
39		let (id, mut flags) = match parse_header_result {
40			Ok(None) => {
41				// Stop reading
42				return Ok(Self::Eof);
43			},
44			Ok(Some(some)) => some,
45			Err(err) => {
46				match parse_options.parsing_mode {
47					ParsingMode::Strict => return Err(err),
48					ParsingMode::BestAttempt | ParsingMode::Relaxed => {
49						log::warn!("Failed to read frame header, skipping: {}", err);
50
51						// Skip this frame and continue reading
52						skip_frame(reader, size)?;
53						return Ok(Self::Skip);
54					},
55				}
56			},
57		};
58
59		if !parse_options.read_cover_art && id == ATTACHED_PICTURE_ID {
60			skip_frame(reader, size)?;
61			return Ok(Self::Skip);
62		}
63
64		if size == 0 {
65			if parse_options.parsing_mode == ParsingMode::Strict {
66				return Err(Id3v2Error::new(Id3v2ErrorKind::EmptyFrame(id)).into());
67			}
68
69			log::debug!("Encountered a zero length frame, skipping");
70
71			skip_frame(reader, size)?;
72			return Ok(Self::Skip);
73		}
74
75		// Get the encryption method symbol
76		if let Some(enc) = flags.encryption.as_mut() {
77			log::trace!("Reading encryption method symbol");
78
79			if size < 1 {
80				return Err(Id3v2Error::new(Id3v2ErrorKind::BadFrameLength).into());
81			}
82
83			*enc = reader.read_u8()?;
84			size -= 1;
85		}
86
87		// Get the group identifier
88		if let Some(group) = flags.grouping_identity.as_mut() {
89			log::trace!("Reading group identifier");
90
91			if size < 1 {
92				return Err(Id3v2Error::new(Id3v2ErrorKind::BadFrameLength).into());
93			}
94
95			*group = reader.read_u8()?;
96			size -= 1;
97		}
98
99		// Get the real data length
100		if flags.data_length_indicator.is_some() || flags.compression {
101			log::trace!("Reading data length indicator");
102
103			if size < 4 {
104				return Err(Id3v2Error::new(Id3v2ErrorKind::BadFrameLength).into());
105			}
106
107			// For some reason, no one can follow the spec, so while a data length indicator is *written*
108			// the flag **isn't always set**
109			let len = reader.read_u32::<BigEndian>()?.unsynch();
110			flags.data_length_indicator = Some(len);
111			size -= 4;
112		}
113
114		// Frames must have at least 1 byte, *after* all of the additional data flags can provide
115		if size == 0 {
116			return Err(Id3v2Error::new(Id3v2ErrorKind::BadFrameLength).into());
117		}
118
119		// Restrict the reader to the frame content
120		let mut reader = reader.take(u64::from(size));
121
122		// It seems like the flags are applied in the order:
123		//
124		// unsynchronization -> compression -> encryption
125		//
126		// Which all have their own needs, so this gets a little messy...
127		match flags {
128			// Possible combinations:
129			//
130			// * unsynchronized + compressed + encrypted
131			// * unsynchronized + compressed
132			// * unsynchronized + encrypted
133			// * unsynchronized
134			FrameFlags {
135				unsynchronisation: true,
136				..
137			} => {
138				let mut unsynchronized_reader = UnsynchronizedStream::new(reader);
139
140				if flags.compression {
141					let mut compression_reader = handle_compression(unsynchronized_reader)?;
142
143					if flags.encryption.is_some() {
144						return handle_encryption(&mut compression_reader, size, id, flags);
145					}
146
147					return parse_frame(
148						&mut compression_reader,
149						size,
150						id,
151						flags,
152						version,
153						parse_options.parsing_mode,
154					);
155				}
156
157				if flags.encryption.is_some() {
158					return handle_encryption(&mut unsynchronized_reader, size, id, flags);
159				}
160
161				return parse_frame(
162					&mut unsynchronized_reader,
163					size,
164					id,
165					flags,
166					version,
167					parse_options.parsing_mode,
168				);
169			},
170			// Possible combinations:
171			//
172			// * compressed + encrypted
173			// * compressed
174			FrameFlags {
175				compression: true, ..
176			} => {
177				let mut compression_reader = handle_compression(reader)?;
178
179				if flags.encryption.is_some() {
180					return handle_encryption(&mut compression_reader, size, id, flags);
181				}
182
183				return parse_frame(
184					&mut compression_reader,
185					size,
186					id,
187					flags,
188					version,
189					parse_options.parsing_mode,
190				);
191			},
192			// Possible combinations:
193			//
194			// * encrypted
195			FrameFlags {
196				encryption: Some(_),
197				..
198			} => {
199				return handle_encryption(&mut reader, size, id, flags);
200			},
201			// Everything else that doesn't have special flags
202			_ => {
203				return parse_frame(
204					&mut reader,
205					size,
206					id,
207					flags,
208					version,
209					parse_options.parsing_mode,
210				);
211			},
212		}
213	}
214}
215
216#[cfg(feature = "id3v2_compression_support")]
217#[allow(clippy::unnecessary_wraps)]
218fn handle_compression<R: Read>(reader: R) -> Result<flate2::read::ZlibDecoder<R>> {
219	Ok(flate2::read::ZlibDecoder::new(reader))
220}
221
222#[cfg(not(feature = "id3v2_compression_support"))]
223#[allow(clippy::unnecessary_wraps)]
224fn handle_compression<R>(_: R) -> Result<std::io::Empty> {
225	Err(Id3v2Error::new(Id3v2ErrorKind::CompressedFrameEncountered).into())
226}
227
228fn handle_encryption<R: Read>(
229	reader: &mut R,
230	size: u32,
231	id: FrameId<'static>,
232	flags: FrameFlags,
233) -> Result<ParsedFrame<'static>> {
234	if flags.data_length_indicator.is_none() {
235		return Err(Id3v2Error::new(Id3v2ErrorKind::MissingDataLengthIndicator).into());
236	}
237
238	let mut content = try_vec![0; size as usize];
239	reader.read_exact(&mut content)?;
240
241	let encrypted_frame = Frame::Binary(BinaryFrame {
242		header: FrameHeader::new(id, flags),
243		data: content,
244	});
245
246	// Nothing further we can do with encrypted frames
247	Ok(ParsedFrame::Next(encrypted_frame))
248}
249
250fn parse_frame<R: Read>(
251	reader: &mut R,
252	size: u32,
253	id: FrameId<'static>,
254	flags: FrameFlags,
255	version: Id3v2Version,
256	parse_mode: ParsingMode,
257) -> Result<ParsedFrame<'static>> {
258	match parse_content(reader, id, flags, version, parse_mode)? {
259		Some(frame) => Ok(ParsedFrame::Next(frame)),
260		None => {
261			skip_frame(reader, size)?;
262			Ok(ParsedFrame::Skip)
263		},
264	}
265}
266
267// Note that this is only ever given the full frame size.
268//
269// In the context of `ParsedFrame::read`, the reader is restricted to the frame content, so this
270// is a safe operation, regardless of where we are in parsing the frame.
271//
272// This assumption *CANNOT* be made in other contexts.
273fn skip_frame(reader: &mut impl Read, size: u32) -> Result<()> {
274	log::trace!("Skipping frame of size {}", size);
275
276	let size = u64::from(size);
277	let mut reader = reader.take(size);
278	let skipped = std::io::copy(&mut reader, &mut std::io::sink())?;
279	debug_assert!(skipped <= size);
280
281	Ok(())
282}