1 use crate::storage::{PlainStorage, Storage};
2 use crate::stream::{frame, unsynch};
3 use crate::tag::{Tag, Version};
4 use crate::{Error, ErrorKind};
5 use bitflags::bitflags;
6 use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
7 use std::cmp;
8 use std::fs;
9 use std::io::{self, Read, Write};
10 use std::ops::Range;
11 use std::path::Path;
12 
13 static DEFAULT_FILE_DISCARD: &[&str] = &[
14     "AENC", "ETCO", "EQUA", "MLLT", "POSS", "SYLT", "SYTC", "RVAD", "TENC", "TLEN", "TSIZ",
15 ];
16 
17 bitflags! {
18     struct Flags: u8 {
19         const UNSYNCHRONISATION = 0x80; // All versions
20         const COMPRESSION       = 0x40; // =ID3v2.2
21         const EXTENDED_HEADER   = 0x40; // >ID3v2.3, duplicate with TAG_COMPRESSION :(
22         const EXPERIMENTAL      = 0x20; // >ID3v2.3
23         const FOOTER            = 0x10; // >ID3v2.4
24     }
25 }
26 
27 struct Header {
28     version: Version,
29     flags: Flags,
30     tag_size: u32,
31     // TODO: Extended header.
32 }
33 
34 impl Header {
size(&self) -> u6435     fn size(&self) -> u64 {
36         10 // Raw header.
37     }
38 
frame_bytes(&self) -> u6439     fn frame_bytes(&self) -> u64 {
40         u64::from(self.tag_size)
41     }
42 
tag_size(&self) -> u6443     fn tag_size(&self) -> u64 {
44         self.size() + self.frame_bytes()
45     }
46 }
47 
48 impl Header {
decode(mut reader: impl io::Read) -> crate::Result<Header>49     fn decode(mut reader: impl io::Read) -> crate::Result<Header> {
50         let mut header = [0; 10];
51         let nread = reader.read(&mut header)?;
52         if nread < header.len() || &header[0..3] != b"ID3" {
53             return Err(Error::new(
54                 ErrorKind::NoTag,
55                 "reader does not contain an id3 tag",
56             ));
57         }
58 
59         let (ver_major, ver_minor) = (header[3], header[4]);
60         let version = match (ver_major, ver_minor) {
61             (2, _) => Version::Id3v22,
62             (3, _) => Version::Id3v23,
63             (4, _) => Version::Id3v24,
64             (_, _) => {
65                 return Err(Error::new(
66                     ErrorKind::UnsupportedVersion(ver_major, ver_minor),
67                     "unsupported id3 tag version",
68                 ));
69             }
70         };
71         let flags = Flags::from_bits(header[5])
72             .ok_or_else(|| Error::new(ErrorKind::Parsing, "unknown tag header flags are set"))?;
73         let tag_size = unsynch::decode_u32(BigEndian::read_u32(&header[6..10]));
74 
75         // compression only exists on 2.2 and conflicts with 2.3+'s extended header
76         if version == Version::Id3v22 && flags.contains(Flags::COMPRESSION) {
77             return Err(Error::new(
78                 ErrorKind::UnsupportedFeature,
79                 "id3v2.2 compression is not supported",
80             ));
81         }
82 
83         // TODO: actually use the extended header data.
84         if flags.contains(Flags::EXTENDED_HEADER) {
85             let ext_size = unsynch::decode_u32(reader.read_u32::<BigEndian>()?) as usize;
86             // the extended header size includes itself
87             if ext_size < 6 {
88                 return Err(Error::new(
89                     ErrorKind::Parsing,
90                     "Extended header has a minimum size of 6",
91                 ));
92             }
93             let ext_remaining_size = ext_size - 4;
94             let mut ext_header = Vec::with_capacity(cmp::min(ext_remaining_size, 0xffff));
95             reader
96                 .by_ref()
97                 .take(ext_remaining_size as u64)
98                 .read_to_end(&mut ext_header)?;
99             if flags.contains(Flags::UNSYNCHRONISATION) {
100                 unsynch::decode_vec(&mut ext_header);
101             }
102         }
103 
104         Ok(Header {
105             version,
106             flags,
107             tag_size,
108         })
109     }
110 }
111 
decode(mut reader: impl io::Read) -> crate::Result<Tag>112 pub fn decode(mut reader: impl io::Read) -> crate::Result<Tag> {
113     let header = Header::decode(&mut reader)?;
114 
115     if header.version == Version::Id3v22 {
116         // Limit the reader only to the given tag_size, don't return any more bytes after that.
117         let v2_reader = reader.take(header.frame_bytes());
118 
119         if header.flags.contains(Flags::UNSYNCHRONISATION) {
120             // Unwrap all 'unsynchronized' bytes in the tag before parsing frames.
121             decode_v2_frames(unsynch::Reader::new(v2_reader))
122         } else {
123             decode_v2_frames(v2_reader)
124         }
125     } else {
126         let mut offset = 0;
127         let mut tag = Tag::new();
128         while offset < header.frame_bytes() {
129             let rs = frame::decode(
130                 &mut reader,
131                 header.version,
132                 header.flags.contains(Flags::UNSYNCHRONISATION),
133             );
134             let v = match rs {
135                 Ok(v) => v,
136                 Err(err) => return Err(err.with_tag(tag)),
137             };
138             let (bytes_read, frame) = match v {
139                 Some(v) => v,
140                 None => break, // Padding.
141             };
142             tag.add_frame(frame);
143             offset += bytes_read as u64;
144         }
145         Ok(tag)
146     }
147 }
148 
decode_v2_frames(mut reader: impl io::Read) -> crate::Result<Tag>149 pub fn decode_v2_frames(mut reader: impl io::Read) -> crate::Result<Tag> {
150     let mut tag = Tag::new();
151     // Add all frames, until either an error is thrown or there are no more frames to parse
152     // (because of EOF or a Padding).
153     loop {
154         let v = match frame::v2::decode(&mut reader) {
155             Ok(v) => v,
156             Err(err) => return Err(err.with_tag(tag)),
157         };
158         match v {
159             Some((_bytes_read, frame)) => {
160                 tag.add_frame(frame);
161             }
162             None => break Ok(tag),
163         }
164     }
165 }
166 
167 /// The `Encoder` may be used to encode tags.
168 #[derive(Clone, Debug)]
169 pub struct Encoder {
170     version: Version,
171     unsynchronisation: bool,
172     compression: bool,
173     file_altered: bool,
174 }
175 
176 impl Encoder {
177     /// Constructs a new `Encoder` with the following configuration:
178     ///
179     /// * version is ID3v2.4
180     /// * unsynchronization is disabled due to compatibility issues
181     /// * no compression
182     /// * file is not marked as altered
new() -> Self183     pub fn new() -> Self {
184         Self {
185             version: Version::Id3v24,
186             unsynchronisation: false,
187             compression: false,
188             file_altered: false,
189         }
190     }
191 
192     /// Sets the ID3 version.
version(mut self, version: Version) -> Self193     pub fn version(mut self, version: Version) -> Self {
194         self.version = version;
195         self
196     }
197 
198     /// Enables or disables the unsynchronisation scheme.
199     ///
200     /// This avoids patterns that resemble MP3-frame headers from being
201     /// encoded. If you are encoding to MP3 files and wish to be compatible
202     /// with very old tools, you probably want this enabled.
unsynchronisation(mut self, unsynchronisation: bool) -> Self203     pub fn unsynchronisation(mut self, unsynchronisation: bool) -> Self {
204         self.unsynchronisation = unsynchronisation;
205         self
206     }
207 
208     /// Enables or disables compression.
compression(mut self, compression: bool) -> Self209     pub fn compression(mut self, compression: bool) -> Self {
210         self.compression = compression;
211         self
212     }
213 
214     /// Informs the encoder whether the file this tag belongs to has been changed.
215     ///
216     /// This subsequently discards any tags that have their File Alter Preservation bits set and
217     /// that have a relation to the file contents:
218     ///
219     ///   AENC, ETCO, EQUA, MLLT, POSS, SYLT, SYTC, RVAD, TENC, TLEN, TSIZ
file_altered(mut self, file_altered: bool) -> Self220     pub fn file_altered(mut self, file_altered: bool) -> Self {
221         self.file_altered = file_altered;
222         self
223     }
224 
225     /// Encodes the specified tag using the settings set in the encoder.
226     ///
227     /// Note that the plain tag is written, regardless of the original contents. To safely encode a
228     /// tag to an MP3 file, use `Encoder::encode_to_path`.
encode(&self, tag: &Tag, mut writer: impl io::Write) -> crate::Result<()>229     pub fn encode(&self, tag: &Tag, mut writer: impl io::Write) -> crate::Result<()> {
230         // remove frames which have the flags indicating they should be removed
231         let saved_frames = tag
232             .frames()
233             // Assert that by encoding, we are changing the tag. If the Tag Alter Preservation bit
234             // is set, discard the frame.
235             .filter(|frame| !frame.tag_alter_preservation())
236             // If the file this tag belongs to is updated, check for the File Alter Preservation
237             // bit.
238             .filter(|frame| !self.file_altered || !frame.file_alter_preservation())
239             // Check whether this frame is part of the set of frames that should always be
240             // discarded when the file is changed.
241             .filter(|frame| !self.file_altered || !DEFAULT_FILE_DISCARD.contains(&frame.id()));
242 
243         let mut flags = Flags::empty();
244         flags.set(Flags::UNSYNCHRONISATION, self.unsynchronisation);
245         if self.version == Version::Id3v22 {
246             flags.set(Flags::COMPRESSION, self.compression);
247         }
248 
249         let mut frame_data = Vec::new();
250         for frame in saved_frames {
251             frame::encode(&mut frame_data, frame, self.version, self.unsynchronisation)?;
252         }
253         // In ID3v2, Unsynchronization is applied to the whole tag data at once, not for each frame
254         // separately.
255         if self.version == Version::Id3v22 && self.unsynchronisation {
256             unsynch::encode_vec(&mut frame_data)
257         }
258         let tag_size = 10 + frame_data.len() + 1;
259         writer.write_all(b"ID3")?;
260         writer.write_all(&[self.version.minor() as u8, 0])?;
261         writer.write_u8(flags.bits())?;
262         writer.write_u32::<BigEndian>(unsynch::encode_u32(tag_size as u32))?;
263         writer.write_all(&frame_data[..])?;
264         writer.write_u8(0)?;
265         Ok(())
266     }
267 
268     /// Encodes a tag and replaces any existing tag in the file pointed to by the specified path.
encode_to_path(&self, tag: &Tag, path: impl AsRef<Path>) -> crate::Result<()>269     pub fn encode_to_path(&self, tag: &Tag, path: impl AsRef<Path>) -> crate::Result<()> {
270         let mut file = fs::OpenOptions::new().read(true).write(true).open(path)?;
271         #[allow(clippy::reversed_empty_ranges)]
272         let location = locate_id3v2(&mut file)?.unwrap_or(0..0); // Create a new tag if none could be located.
273 
274         let mut storage = PlainStorage::new(file, location);
275         let mut w = storage.writer()?;
276         self.encode(tag, &mut w)?;
277         w.flush()?;
278         Ok(())
279     }
280 }
281 
282 impl Default for Encoder {
default() -> Self283     fn default() -> Self {
284         Self::new()
285     }
286 }
287 
locate_id3v2(mut reader: impl io::Read + io::Seek) -> crate::Result<Option<Range<u64>>>288 pub fn locate_id3v2(mut reader: impl io::Read + io::Seek) -> crate::Result<Option<Range<u64>>> {
289     let header = match Header::decode(&mut reader) {
290         Ok(v) => v,
291         Err(err) => match err.kind {
292             ErrorKind::NoTag => return Ok(None),
293             _ => return Err(err),
294         },
295     };
296 
297     let tag_size = header.tag_size();
298     reader.seek(io::SeekFrom::Start(tag_size))?;
299     let num_padding = reader
300         .bytes()
301         .take_while(|rs| rs.as_ref().map(|b| *b == 0x00).unwrap_or(false))
302         .count();
303     eprintln!(
304         "location: size=0x{:x} padding=0x{:x}",
305         tag_size, num_padding
306     );
307     Ok(Some(0..tag_size + num_padding as u64))
308 }
309 
310 #[cfg(all(test, feature = "unstable"))]
311 mod benchmarks {
312     use super::*;
313     use std::fs;
314 
315     #[bench]
read_id3v23(b: &mut test::Bencher)316     fn read_id3v23(b: &mut test::Bencher) {
317         let mut buf = Vec::new();
318         fs::File::open("testdata/id3v23.id3")
319             .unwrap()
320             .read_to_end(&mut buf)
321             .unwrap();
322         b.iter(|| {
323             decode(&mut io::Cursor::new(buf.as_slice())).unwrap();
324         });
325     }
326 
327     #[bench]
read_id3v24(b: &mut test::Bencher)328     fn read_id3v24(b: &mut test::Bencher) {
329         let mut buf = Vec::new();
330         fs::File::open("testdata/id3v24.id3")
331             .unwrap()
332             .read_to_end(&mut buf)
333             .unwrap();
334         b.iter(|| {
335             decode(&mut io::Cursor::new(buf.as_slice())).unwrap();
336         });
337     }
338 }
339 
340 #[cfg(test)]
341 mod tests {
342     use super::*;
343     use crate::frame::{
344         Content, Frame, Picture, PictureType, SynchronisedLyrics, SynchronisedLyricsType,
345         TimestampFormat,
346     };
347     use std::fs;
348     use std::io::{self, Read};
349 
make_tag() -> Tag350     fn make_tag() -> Tag {
351         let mut tag = Tag::new();
352         tag.set_title("Title");
353         tag.set_artist("Artist");
354         tag.set_genre("Genre");
355         tag.set_duration(1337);
356         let mut image_data = Vec::new();
357         fs::File::open("testdata/image.jpg")
358             .unwrap()
359             .read_to_end(&mut image_data)
360             .unwrap();
361         tag.add_picture(Picture {
362             mime_type: "image/jpeg".to_string(),
363             picture_type: PictureType::CoverFront,
364             description: "an image".to_string(),
365             data: image_data,
366         });
367         tag.add_synchronised_lyrics(SynchronisedLyrics {
368             lang: "eng".to_string(),
369             timestamp_format: TimestampFormat::MS,
370             content_type: SynchronisedLyricsType::Lyrics,
371             content: vec![
372                 (1000, "he".to_string()),
373                 (1100, "llo".to_string()),
374                 (1200, "world".to_string()),
375             ],
376         });
377         tag
378     }
379 
380     #[test]
read_id3v22()381     fn read_id3v22() {
382         let mut file = fs::File::open("testdata/id3v22.id3").unwrap();
383         let tag: Tag = decode(&mut file).unwrap();
384         assert_eq!("Henry Frottey INTRO", tag.title().unwrap());
385         assert_eq!("Hörbuch & Gesprochene Inhalte", tag.genre().unwrap());
386         assert_eq!(1, tag.disc().unwrap());
387         assert_eq!(27, tag.total_discs().unwrap());
388         assert_eq!(2015, tag.year().unwrap());
389         assert_eq!(
390             PictureType::Other,
391             tag.pictures().nth(0).unwrap().picture_type
392         );
393         assert_eq!("", tag.pictures().nth(0).unwrap().description);
394         assert_eq!("image/jpeg", tag.pictures().nth(0).unwrap().mime_type);
395     }
396 
397     #[test]
read_id3v23()398     fn read_id3v23() {
399         let mut file = fs::File::open("testdata/id3v23.id3").unwrap();
400         let tag = decode(&mut file).unwrap();
401         assert_eq!("Title", tag.title().unwrap());
402         assert_eq!("Genre", tag.genre().unwrap());
403         assert_eq!(1, tag.disc().unwrap());
404         assert_eq!(1, tag.total_discs().unwrap());
405         assert_eq!(
406             PictureType::CoverFront,
407             tag.pictures().nth(0).unwrap().picture_type
408         );
409     }
410 
411     #[test]
read_id3v24()412     fn read_id3v24() {
413         let mut file = fs::File::open("testdata/id3v24.id3").unwrap();
414         let tag = decode(&mut file).unwrap();
415         assert_eq!("Title", tag.title().unwrap());
416         assert_eq!(1, tag.disc().unwrap());
417         assert_eq!(1, tag.total_discs().unwrap());
418         assert_eq!(
419             PictureType::CoverFront,
420             tag.pictures().nth(0).unwrap().picture_type
421         );
422     }
423 
424     #[test]
read_id3v24_extended()425     fn read_id3v24_extended() {
426         let mut file = fs::File::open("testdata/id3v24_ext.id3").unwrap();
427         let tag = decode(&mut file).unwrap();
428         assert_eq!("Title", tag.title().unwrap());
429         assert_eq!("Genre", tag.genre().unwrap());
430         assert_eq!("Artist", tag.artist().unwrap());
431         assert_eq!("Album", tag.album().unwrap());
432         assert_eq!(2, tag.track().unwrap());
433     }
434 
435     #[test]
write_id3v22()436     fn write_id3v22() {
437         let tag = make_tag();
438         let mut buffer = Vec::new();
439         Encoder::new()
440             .version(Version::Id3v22)
441             .encode(&tag, &mut buffer)
442             .unwrap();
443         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
444         assert_eq!(tag, tag_read);
445     }
446 
447     #[test]
write_id3v22_unsynch()448     fn write_id3v22_unsynch() {
449         let tag = make_tag();
450         let mut buffer = Vec::new();
451         Encoder::new()
452             .unsynchronisation(true)
453             .version(Version::Id3v22)
454             .encode(&tag, &mut buffer)
455             .unwrap();
456         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
457         assert_eq!(tag, tag_read);
458     }
459 
460     #[test]
write_id3v22_invalid_id()461     fn write_id3v22_invalid_id() {
462         let mut tag = make_tag();
463         tag.add_frame(Frame::with_content("XXX", Content::Unknown(vec![1, 2, 3])));
464         tag.add_frame(Frame::with_content("YYY", Content::Unknown(vec![4, 5, 6])));
465         tag.add_frame(Frame::with_content("ZZZ", Content::Unknown(vec![7, 8, 9])));
466         let mut buffer = Vec::new();
467         Encoder::new()
468             .version(Version::Id3v22)
469             .encode(&tag, &mut buffer)
470             .unwrap();
471         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
472         assert_eq!(tag, tag_read);
473     }
474 
475     #[test]
write_id3v23()476     fn write_id3v23() {
477         let tag = make_tag();
478         let mut buffer = Vec::new();
479         Encoder::new()
480             .version(Version::Id3v23)
481             .encode(&tag, &mut buffer)
482             .unwrap();
483         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
484         assert_eq!(tag, tag_read);
485     }
486 
487     #[test]
write_id3v23_compression()488     fn write_id3v23_compression() {
489         let tag = make_tag();
490         let mut buffer = Vec::new();
491         Encoder::new()
492             .compression(true)
493             .version(Version::Id3v23)
494             .encode(&tag, &mut buffer)
495             .unwrap();
496         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
497         assert_eq!(tag, tag_read);
498     }
499 
500     #[test]
write_id3v23_unsynch()501     fn write_id3v23_unsynch() {
502         let tag = make_tag();
503         let mut buffer = Vec::new();
504         Encoder::new()
505             .unsynchronisation(true)
506             .version(Version::Id3v23)
507             .encode(&tag, &mut buffer)
508             .unwrap();
509         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
510         assert_eq!(tag, tag_read);
511     }
512 
513     #[test]
write_id3v24()514     fn write_id3v24() {
515         let tag = make_tag();
516         let mut buffer = Vec::new();
517         Encoder::new()
518             .version(Version::Id3v24)
519             .encode(&tag, &mut buffer)
520             .unwrap();
521         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
522         assert_eq!(tag, tag_read);
523     }
524 
525     #[test]
write_id3v24_compression()526     fn write_id3v24_compression() {
527         let tag = make_tag();
528         let mut buffer = Vec::new();
529         Encoder::new()
530             .compression(true)
531             .version(Version::Id3v24)
532             .encode(&tag, &mut buffer)
533             .unwrap();
534         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
535         assert_eq!(tag, tag_read);
536     }
537 
538     #[test]
write_id3v24_unsynch()539     fn write_id3v24_unsynch() {
540         let tag = make_tag();
541         let mut buffer = Vec::new();
542         Encoder::new()
543             .unsynchronisation(true)
544             .version(Version::Id3v24)
545             .encode(&tag, &mut buffer)
546             .unwrap();
547         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
548         assert_eq!(tag, tag_read);
549     }
550 
551     #[test]
write_id3v24_alter_file()552     fn write_id3v24_alter_file() {
553         let mut tag = Tag::new();
554         tag.set_duration(1337);
555 
556         let mut buffer = Vec::new();
557         Encoder::new()
558             .version(Version::Id3v24)
559             .file_altered(true)
560             .encode(&tag, &mut buffer)
561             .unwrap();
562 
563         let tag_read = decode(&mut io::Cursor::new(buffer)).unwrap();
564         assert!(tag_read.get("TLEN").is_none());
565     }
566 
567     #[test]
test_locate_id3v22()568     fn test_locate_id3v22() {
569         let file = fs::File::open("testdata/id3v22.id3").unwrap();
570         let location = locate_id3v2(file).unwrap();
571         assert_eq!(Some(0..0x0000c3ea), location);
572     }
573 
574     #[test]
test_locate_id3v23()575     fn test_locate_id3v23() {
576         let file = fs::File::open("testdata/id3v23.id3").unwrap();
577         let location = locate_id3v2(file).unwrap();
578         assert_eq!(Some(0..0x00006c0a), location);
579     }
580 
581     #[test]
test_locate_id3v24()582     fn test_locate_id3v24() {
583         let file = fs::File::open("testdata/id3v24.id3").unwrap();
584         let location = locate_id3v2(file).unwrap();
585         assert_eq!(Some(0..0x00006c0a), location);
586     }
587 
588     #[test]
test_locate_id3v24_ext()589     fn test_locate_id3v24_ext() {
590         let file = fs::File::open("testdata/id3v24_ext.id3").unwrap();
591         let location = locate_id3v2(file).unwrap();
592         assert_eq!(Some(0..0x0000018d), location);
593     }
594 
595     #[test]
test_locate_no_tag()596     fn test_locate_no_tag() {
597         let file = fs::File::open("testdata/mpeg-header").unwrap();
598         let location = locate_id3v2(file).unwrap();
599         assert_eq!(None, location);
600     }
601 }
602