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