1 /***************************************************************************
2     copyright            : (C) 2002 - 2008 by Scott Wheeler
3     email                : wheeler@kde.org
4  ***************************************************************************/
5 
6 /***************************************************************************
7  *   This library is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU Lesser General Public License version   *
9  *   2.1 as published by the Free Software Foundation.                     *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful, but   *
12  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the Free Software   *
18  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA         *
19  *   02110-1301  USA                                                       *
20  *                                                                         *
21  *   Alternatively, this file is available under the Mozilla Public        *
22  *   License Version 1.1.  You may obtain a copy of the License at         *
23  *   http://www.mozilla.org/MPL/                                           *
24  ***************************************************************************/
25 
26 #include <tdebug.h>
27 #include <tzlib.h>
28 
29 #include "id3v2framefactory.h"
30 #include "id3v2synchdata.h"
31 #include "id3v1genres.h"
32 
33 #include "frames/attachedpictureframe.h"
34 #include "frames/commentsframe.h"
35 #include "frames/relativevolumeframe.h"
36 #include "frames/textidentificationframe.h"
37 #include "frames/uniquefileidentifierframe.h"
38 #include "frames/unknownframe.h"
39 #include "frames/generalencapsulatedobjectframe.h"
40 #include "frames/urllinkframe.h"
41 #include "frames/unsynchronizedlyricsframe.h"
42 #include "frames/popularimeterframe.h"
43 #include "frames/privateframe.h"
44 #include "frames/ownershipframe.h"
45 #include "frames/synchronizedlyricsframe.h"
46 #include "frames/eventtimingcodesframe.h"
47 #include "frames/chapterframe.h"
48 #include "frames/tableofcontentsframe.h"
49 #include "frames/podcastframe.h"
50 
51 using namespace TagLib;
52 using namespace ID3v2;
53 
54 namespace
55 {
updateGenre(TextIdentificationFrame * frame)56   void updateGenre(TextIdentificationFrame *frame)
57   {
58     StringList fields = frame->fieldList();
59     StringList newfields;
60 
61     for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
62       String s = *it;
63       int end = s.find(")");
64 
65       if(s.startsWith("(") && end > 0) {
66         // "(12)Genre"
67         String text = s.substr(end + 1);
68         bool ok;
69         int number = s.substr(1, end - 1).toInt(&ok);
70         if(ok && number >= 0 && number <= 255 && !(ID3v1::genre(number) == text))
71           newfields.append(s.substr(1, end - 1));
72         if(!text.isEmpty())
73           newfields.append(text);
74       }
75       else {
76         // "Genre" or "12"
77         newfields.append(s);
78       }
79     }
80 
81     if(newfields.isEmpty())
82       fields.append(String());
83 
84     frame->setText(newfields);
85   }
86 }
87 
88 class FrameFactory::FrameFactoryPrivate
89 {
90 public:
FrameFactoryPrivate()91   FrameFactoryPrivate() :
92     defaultEncoding(String::Latin1),
93     useDefaultEncoding(false) {}
94 
95   String::Type defaultEncoding;
96   bool useDefaultEncoding;
97 
setTextEncoding(T * frame)98   template <class T> void setTextEncoding(T *frame)
99   {
100     if(useDefaultEncoding)
101       frame->setTextEncoding(defaultEncoding);
102   }
103 };
104 
105 FrameFactory FrameFactory::factory;
106 
107 ////////////////////////////////////////////////////////////////////////////////
108 // public members
109 ////////////////////////////////////////////////////////////////////////////////
110 
instance()111 FrameFactory *FrameFactory::instance()
112 {
113   return &factory;
114 }
115 
createFrame(const ByteVector & data,bool synchSafeInts) const116 Frame *FrameFactory::createFrame(const ByteVector &data, bool synchSafeInts) const
117 {
118   return createFrame(data, static_cast<unsigned int>(synchSafeInts ? 4 : 3));
119 }
120 
createFrame(const ByteVector & data,unsigned int version) const121 Frame *FrameFactory::createFrame(const ByteVector &data, unsigned int version) const
122 {
123   Header tagHeader;
124   tagHeader.setMajorVersion(version);
125   return createFrame(data, &tagHeader);
126 }
127 
createFrame(const ByteVector & origData,Header * tagHeader) const128 Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) const
129 {
130     return createFrame(origData, const_cast<const Header *>(tagHeader));
131 }
132 
createFrame(const ByteVector & origData,const Header * tagHeader) const133 Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHeader) const
134 {
135   ByteVector data = origData;
136   unsigned int version = tagHeader->majorVersion();
137   Frame::Header *header = new Frame::Header(data, version);
138   ByteVector frameID = header->frameID();
139 
140   // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
141   // characters.  Also make sure that there is data in the frame.
142 
143   if(frameID.size() != (version < 3 ? 3 : 4) ||
144      header->frameSize() <= static_cast<unsigned int>(header->dataLengthIndicator() ? 4 : 0) ||
145      header->frameSize() > data.size())
146   {
147     delete header;
148     return 0;
149   }
150 
151 #ifndef NO_ITUNES_HACKS
152   if(version == 3 && frameID.size() == 4 && frameID[3] == '\0') {
153     // iTunes v2.3 tags store v2.2 frames - convert now
154     frameID = frameID.mid(0, 3);
155     header->setFrameID(frameID);
156     header->setVersion(2);
157     updateFrame(header);
158     header->setVersion(3);
159   }
160 #endif
161 
162   for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
163     if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
164       delete header;
165       return 0;
166     }
167   }
168 
169   if(version > 3 && (tagHeader->unsynchronisation() || header->unsynchronisation())) {
170     // Data lengths are not part of the encoded data, but since they are synch-safe
171     // integers they will be never actually encoded.
172     ByteVector frameData = data.mid(Frame::Header::size(version), header->frameSize());
173     frameData = SynchData::decode(frameData);
174     data = data.mid(0, Frame::Header::size(version)) + frameData;
175   }
176 
177   // TagLib doesn't mess with encrypted frames, so just treat them
178   // as unknown frames.
179 
180   if(!zlib::isAvailable() && header->compression()) {
181     debug("Compressed frames are currently not supported.");
182     return new UnknownFrame(data, header);
183   }
184 
185   if(header->encryption()) {
186     debug("Encrypted frames are currently not supported.");
187     return new UnknownFrame(data, header);
188   }
189 
190   if(!updateFrame(header)) {
191     header->setTagAlterPreservation(true);
192     return new UnknownFrame(data, header);
193   }
194 
195   // updateFrame() might have updated the frame ID.
196 
197   frameID = header->frameID();
198 
199   // This is where things get necissarily nasty.  Here we determine which
200   // Frame subclass (or if none is found simply an Frame) based
201   // on the frame ID.  Since there are a lot of possibilities, that means
202   // a lot of if blocks.
203 
204   // Text Identification (frames 4.2)
205 
206   // Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number), GRP1 (Grouping) are in fact text frames.
207   if(frameID.startsWith("T") || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN" || frameID == "GRP1") {
208 
209     TextIdentificationFrame *f = frameID != "TXXX"
210       ? new TextIdentificationFrame(data, header)
211       : new UserTextIdentificationFrame(data, header);
212 
213     d->setTextEncoding(f);
214 
215     if(frameID == "TCON")
216       updateGenre(f);
217 
218     return f;
219   }
220 
221   // Comments (frames 4.10)
222 
223   if(frameID == "COMM") {
224     CommentsFrame *f = new CommentsFrame(data, header);
225     d->setTextEncoding(f);
226     return f;
227   }
228 
229   // Attached Picture (frames 4.14)
230 
231   if(frameID == "APIC") {
232     AttachedPictureFrame *f = new AttachedPictureFrame(data, header);
233     d->setTextEncoding(f);
234     return f;
235   }
236 
237   // ID3v2.2 Attached Picture
238 
239   if(frameID == "PIC") {
240     AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header);
241     d->setTextEncoding(f);
242     return f;
243   }
244 
245   // Relative Volume Adjustment (frames 4.11)
246 
247   if(frameID == "RVA2")
248     return new RelativeVolumeFrame(data, header);
249 
250   // Unique File Identifier (frames 4.1)
251 
252   if(frameID == "UFID")
253     return new UniqueFileIdentifierFrame(data, header);
254 
255   // General Encapsulated Object (frames 4.15)
256 
257   if(frameID == "GEOB") {
258     GeneralEncapsulatedObjectFrame *f = new GeneralEncapsulatedObjectFrame(data, header);
259     d->setTextEncoding(f);
260     return f;
261   }
262 
263   // URL link (frames 4.3)
264 
265   if(frameID.startsWith("W")) {
266     if(frameID != "WXXX") {
267       return new UrlLinkFrame(data, header);
268     }
269     else {
270       UserUrlLinkFrame *f = new UserUrlLinkFrame(data, header);
271       d->setTextEncoding(f);
272       return f;
273     }
274   }
275 
276   // Unsynchronized lyric/text transcription (frames 4.8)
277 
278   if(frameID == "USLT") {
279     UnsynchronizedLyricsFrame *f = new UnsynchronizedLyricsFrame(data, header);
280     if(d->useDefaultEncoding)
281       f->setTextEncoding(d->defaultEncoding);
282     return f;
283   }
284 
285   // Synchronised lyrics/text (frames 4.9)
286 
287   if(frameID == "SYLT") {
288     SynchronizedLyricsFrame *f = new SynchronizedLyricsFrame(data, header);
289     if(d->useDefaultEncoding)
290       f->setTextEncoding(d->defaultEncoding);
291     return f;
292   }
293 
294   // Event timing codes (frames 4.5)
295 
296   if(frameID == "ETCO")
297     return new EventTimingCodesFrame(data, header);
298 
299   // Popularimeter (frames 4.17)
300 
301   if(frameID == "POPM")
302     return new PopularimeterFrame(data, header);
303 
304   // Private (frames 4.27)
305 
306   if(frameID == "PRIV")
307     return new PrivateFrame(data, header);
308 
309   // Ownership (frames 4.22)
310 
311   if(frameID == "OWNE") {
312     OwnershipFrame *f = new OwnershipFrame(data, header);
313     d->setTextEncoding(f);
314     return f;
315   }
316 
317   // Chapter (ID3v2 chapters 1.0)
318 
319   if(frameID == "CHAP")
320     return new ChapterFrame(tagHeader, data, header);
321 
322   // Table of contents (ID3v2 chapters 1.0)
323 
324   if(frameID == "CTOC")
325     return new TableOfContentsFrame(tagHeader, data, header);
326 
327   // Apple proprietary PCST (Podcast)
328 
329   if(frameID == "PCST")
330     return new PodcastFrame(data, header);
331 
332   return new UnknownFrame(data, header);
333 }
334 
rebuildAggregateFrames(ID3v2::Tag * tag) const335 void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const
336 {
337   if(tag->header()->majorVersion() < 4 &&
338      tag->frameList("TDRC").size() == 1 &&
339      tag->frameList("TDAT").size() == 1)
340   {
341     TextIdentificationFrame *tdrc =
342       dynamic_cast<TextIdentificationFrame *>(tag->frameList("TDRC").front());
343     UnknownFrame *tdat = static_cast<UnknownFrame *>(tag->frameList("TDAT").front());
344 
345     if(tdrc &&
346        tdrc->fieldList().size() == 1 &&
347        tdrc->fieldList().front().size() == 4 &&
348        tdat->data().size() >= 5)
349     {
350       String date(tdat->data().mid(1), String::Type(tdat->data()[0]));
351       if(date.length() == 4) {
352         tdrc->setText(tdrc->toString() + '-' + date.substr(2, 2) + '-' + date.substr(0, 2));
353         if(tag->frameList("TIME").size() == 1) {
354           UnknownFrame *timeframe = static_cast<UnknownFrame *>(tag->frameList("TIME").front());
355           if(timeframe->data().size() >= 5) {
356             String time(timeframe->data().mid(1), String::Type(timeframe->data()[0]));
357             if(time.length() == 4) {
358               tdrc->setText(tdrc->toString() + 'T' + time.substr(0, 2) + ':' + time.substr(2, 2));
359             }
360           }
361         }
362       }
363     }
364   }
365 }
366 
defaultTextEncoding() const367 String::Type FrameFactory::defaultTextEncoding() const
368 {
369   return d->defaultEncoding;
370 }
371 
setDefaultTextEncoding(String::Type encoding)372 void FrameFactory::setDefaultTextEncoding(String::Type encoding)
373 {
374   d->useDefaultEncoding = true;
375   d->defaultEncoding = encoding;
376 }
377 
378 ////////////////////////////////////////////////////////////////////////////////
379 // protected members
380 ////////////////////////////////////////////////////////////////////////////////
381 
FrameFactory()382 FrameFactory::FrameFactory() :
383   d(new FrameFactoryPrivate())
384 {
385 }
386 
~FrameFactory()387 FrameFactory::~FrameFactory()
388 {
389   delete d;
390 }
391 
392 namespace
393 {
394   // Frame conversion table ID3v2.2 -> 2.4
395   const char *frameConversion2[][2] = {
396     { "BUF", "RBUF" },
397     { "CNT", "PCNT" },
398     { "COM", "COMM" },
399     { "CRA", "AENC" },
400     { "ETC", "ETCO" },
401     { "GEO", "GEOB" },
402     { "IPL", "TIPL" },
403     { "MCI", "MCDI" },
404     { "MLL", "MLLT" },
405     { "POP", "POPM" },
406     { "REV", "RVRB" },
407     { "SLT", "SYLT" },
408     { "STC", "SYTC" },
409     { "TAL", "TALB" },
410     { "TBP", "TBPM" },
411     { "TCM", "TCOM" },
412     { "TCO", "TCON" },
413     { "TCP", "TCMP" },
414     { "TCR", "TCOP" },
415     { "TDY", "TDLY" },
416     { "TEN", "TENC" },
417     { "TFT", "TFLT" },
418     { "TKE", "TKEY" },
419     { "TLA", "TLAN" },
420     { "TLE", "TLEN" },
421     { "TMT", "TMED" },
422     { "TOA", "TOAL" },
423     { "TOF", "TOFN" },
424     { "TOL", "TOLY" },
425     { "TOR", "TDOR" },
426     { "TOT", "TOAL" },
427     { "TP1", "TPE1" },
428     { "TP2", "TPE2" },
429     { "TP3", "TPE3" },
430     { "TP4", "TPE4" },
431     { "TPA", "TPOS" },
432     { "TPB", "TPUB" },
433     { "TRC", "TSRC" },
434     { "TRD", "TDRC" },
435     { "TRK", "TRCK" },
436     { "TS2", "TSO2" },
437     { "TSA", "TSOA" },
438     { "TSC", "TSOC" },
439     { "TSP", "TSOP" },
440     { "TSS", "TSSE" },
441     { "TST", "TSOT" },
442     { "TT1", "TIT1" },
443     { "TT2", "TIT2" },
444     { "TT3", "TIT3" },
445     { "TXT", "TOLY" },
446     { "TXX", "TXXX" },
447     { "TYE", "TDRC" },
448     { "UFI", "UFID" },
449     { "ULT", "USLT" },
450     { "WAF", "WOAF" },
451     { "WAR", "WOAR" },
452     { "WAS", "WOAS" },
453     { "WCM", "WCOM" },
454     { "WCP", "WCOP" },
455     { "WPB", "WPUB" },
456     { "WXX", "WXXX" },
457 
458     // Apple iTunes nonstandard frames
459     { "PCS", "PCST" },
460     { "TCT", "TCAT" },
461     { "TDR", "TDRL" },
462     { "TDS", "TDES" },
463     { "TID", "TGID" },
464     { "WFD", "WFED" },
465     { "MVN", "MVNM" },
466     { "MVI", "MVIN" },
467     { "GP1", "GRP1" },
468   };
469   const size_t frameConversion2Size = sizeof(frameConversion2) / sizeof(frameConversion2[0]);
470 
471   // Frame conversion table ID3v2.3 -> 2.4
472   const char *frameConversion3[][2] = {
473     { "TORY", "TDOR" },
474     { "TYER", "TDRC" },
475     { "IPLS", "TIPL" },
476   };
477   const size_t frameConversion3Size = sizeof(frameConversion3) / sizeof(frameConversion3[0]);
478 }
479 
updateFrame(Frame::Header * header) const480 bool FrameFactory::updateFrame(Frame::Header *header) const
481 {
482   const ByteVector frameID = header->frameID();
483 
484   switch(header->version()) {
485 
486   case 2: // ID3v2.2
487   {
488     if(frameID == "CRM" ||
489        frameID == "EQU" ||
490        frameID == "LNK" ||
491        frameID == "RVA" ||
492        frameID == "TIM" ||
493        frameID == "TSI" ||
494        frameID == "TDA")
495     {
496       debug("ID3v2.4 no longer supports the frame type " + String(frameID) +
497             ".  It will be discarded from the tag.");
498       return false;
499     }
500 
501     // ID3v2.2 only used 3 bytes for the frame ID, so we need to convert all of
502     // the frames to their 4 byte ID3v2.4 equivalent.
503 
504     for(size_t i = 0; i < frameConversion2Size; ++i) {
505       if(frameID == frameConversion2[i][0]) {
506         header->setFrameID(frameConversion2[i][1]);
507         break;
508       }
509     }
510 
511     break;
512   }
513 
514   case 3: // ID3v2.3
515   {
516     if(frameID == "EQUA" ||
517        frameID == "RVAD" ||
518        frameID == "TIME" ||
519        frameID == "TRDA" ||
520        frameID == "TSIZ" ||
521        frameID == "TDAT")
522     {
523       debug("ID3v2.4 no longer supports the frame type " + String(frameID) +
524             ".  It will be discarded from the tag.");
525       return false;
526     }
527 
528     for(size_t i = 0; i < frameConversion3Size; ++i) {
529       if(frameID == frameConversion3[i][0]) {
530         header->setFrameID(frameConversion3[i][1]);
531         break;
532       }
533     }
534 
535     break;
536   }
537 
538   default:
539 
540     // This should catch a typo that existed in TagLib up to and including
541     // version 1.1 where TRDC was used for the year rather than TDRC.
542 
543     if(frameID == "TRDC")
544       header->setFrameID("TDRC");
545 
546     break;
547   }
548 
549   return true;
550 }
551