Skip to content

Commit c27264f

Browse files
committed
Verify ID3 Frame contents prior to writing
1 parent e6b714e commit c27264f

File tree

4 files changed

+60
-2
lines changed

4 files changed

+60
-2
lines changed

src/error.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ pub enum LoftyError {
4949
#[cfg(feature = "id3v2")]
5050
/// Arises when invalid data is encountered while reading an ID3v2 synchronized text frame
5151
BadSyncText,
52+
#[cfg(feature = "id3v2")]
53+
/// Arises when attempting to write an invalid Frame (Bad `FrameID`/`FrameValue` pairing)
54+
BadFrame(String, &'static str),
5255
/// Arises when an atom contains invalid data
5356
BadAtom(&'static str),
5457

@@ -131,6 +134,12 @@ impl Display for LoftyError {
131134
),
132135
#[cfg(feature = "id3v2")]
133136
LoftyError::BadSyncText => write!(f, "ID3v2: Encountered invalid data in SYLT frame"),
137+
#[cfg(feature = "id3v2")]
138+
LoftyError::BadFrame(frame_id, frame_value) => write!(
139+
f,
140+
"ID3v2: Attempted to write an invalid frame. ID: \"{}\", Value: \"{}\"",
141+
frame_id, frame_value
142+
),
134143
LoftyError::BadAtom(message) => write!(f, "MP4 Atom: {}", message),
135144

136145
// Files

src/id3/v2/frame/content.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ pub(super) fn parse_content(
117117
"TXXX" => parse_user_defined(content, false)?,
118118
"WXXX" => parse_user_defined(content, true)?,
119119
"COMM" | "USLT" => parse_text_language(content, id)?,
120-
_ if id.starts_with('T') || id == "WFED" => parse_text(content)?,
120+
_ if id.starts_with('T') => parse_text(content)?,
121121
// Apple proprietary frames
122122
"WFED" | "GRP1" => parse_text(content)?,
123123
_ if id.starts_with('W') => parse_link(content)?,

src/id3/v2/tag.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ impl Id3v2Tag {
282282
///
283283
/// * Attempting to write the tag to a format that does not support it
284284
/// * Attempting to write an encrypted frame without a valid method symbol or data length indicator
285+
/// * Attempting to write an invalid [`FrameID`]/[`FrameValue`] pairing
285286
pub fn write_to(&self, file: &mut File) -> Result<()> {
286287
Id3v2TagRef::new(self.flags, self.frames.iter().filter_map(Frame::as_opt_ref))
287288
.write_to(file)
@@ -576,6 +577,26 @@ mod tests {
576577
crate::tag_utils::test_utils::verify_tag(&tag, true, true);
577578
}
578579

580+
#[test]
581+
fn fail_write_bad_frame() {
582+
let mut tag = Id3v2Tag::default();
583+
tag.insert(Frame {
584+
id: FrameID::Valid(String::from("ABCD")),
585+
value: FrameValue::URL(String::from("FOO URL")),
586+
flags: Default::default(),
587+
});
588+
589+
let res = tag.dump_to(&mut Vec::<u8>::new());
590+
591+
assert!(res.is_err());
592+
assert_eq!(
593+
res.unwrap_err().to_string(),
594+
String::from(
595+
"ID3v2: Attempted to write an invalid frame. ID: \"ABCD\", Value: \"URL\""
596+
)
597+
);
598+
}
599+
579600
#[test]
580601
fn tag_to_id3v2() {
581602
fn verify_frame(tag: &Id3v2Tag, id: &str, value: &str) {

src/id3/v2/write/frame.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::error::{LoftyError, Result};
2-
use crate::id3::v2::frame::{FrameFlags, FrameRef};
2+
use crate::id3::v2::frame::{FrameFlags, FrameRef, FrameValue};
33
use crate::id3::v2::synch_u32;
44

55
use std::io::Write;
@@ -14,6 +14,7 @@ where
1414
W: Write,
1515
{
1616
for frame in frames {
17+
verify_frame(&frame)?;
1718
let value = frame.value.as_bytes()?;
1819

1920
write_frame(writer, frame.id, frame.flags, &value)?;
@@ -22,6 +23,33 @@ where
2223
Ok(())
2324
}
2425

26+
fn verify_frame(frame: &FrameRef) -> Result<()> {
27+
match (frame.id, frame.value.as_ref()) {
28+
("APIC", FrameValue::Picture { .. })
29+
| ("USLT", FrameValue::UnSyncText(_))
30+
| ("COMM", FrameValue::Comment(_))
31+
| ("TXXX", FrameValue::UserText(_))
32+
| ("WXXX", FrameValue::UserURL(_))
33+
| (_, FrameValue::Binary(_))
34+
| ("WFED" | "GRP1", FrameValue::Text { .. }) => Ok(()),
35+
(id, FrameValue::Text { .. }) if id.starts_with('T') => Ok(()),
36+
(id, FrameValue::URL(_)) if id.starts_with('W') => Ok(()),
37+
(id, frame_value) => Err(LoftyError::BadFrame(
38+
id.to_string(),
39+
match frame_value {
40+
FrameValue::Comment(_) => "Comment",
41+
FrameValue::UnSyncText(_) => "UnSyncText",
42+
FrameValue::Text { .. } => "Text",
43+
FrameValue::UserText(_) => "UserText",
44+
FrameValue::URL(_) => "URL",
45+
FrameValue::UserURL(_) => "UserURL",
46+
FrameValue::Picture { .. } => "Picture",
47+
FrameValue::Binary(_) => "Binary",
48+
},
49+
)),
50+
}
51+
}
52+
2553
fn write_frame<W>(writer: &mut W, name: &str, flags: FrameFlags, value: &[u8]) -> Result<()>
2654
where
2755
W: Write,

0 commit comments

Comments
 (0)