1 /* Copyright (C) 2001--2005 Chris Vaill
2    This file is part of nid3lib.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA*/
17 
18 #define _POSIX_C_SOURCE 2
19 
20 #include "config.h"
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <errno.h>
29 #include "nid3P.h"
30 
31 /*
32  * Version conversion data
33  */
34 
35 /*
36  * struct frame_convert holds all information for converting a frame
37  * between different versions of tags.
38  *
39  * The from field holds the frame ID as used in the earlier version;
40  * the to field has the equivalent frame ID for the newer version.
41  *
42  * If the converter field is non-NULL, it contains a pointer to a
43  * function which will convert the frame data.
44  *
45  * The converter's arguments are the frame, the two frame ID's from
46  * and to, and an int which, if non-zero, indicates that the
47  * conversion should be done backwards.  If the converter field for a
48  * frame convert is NULL, the data left unchanged (only the frame ID
49  * is changed).
50  *
51  * A converter function should return 0 normally, 1 if the frame
52  * should be deleted instead of converted, or -1 on error.
53  */
54 
55 struct frame_convert {
56   const char *from;
57   const char *to;
58   int (*converter)(id3_frame_t, const char *, const char *, int);
59 };
60 
61 static int convert_link(id3_frame_t, const char *, const char *, int);
62 static int convert_apic(id3_frame_t, const char *, const char *, int);
63 static int convert_time(id3_frame_t, const char *, const char *, int);
64 static int convert_tcon(id3_frame_t, const char *, const char *, int);
65 static int convert_rva(id3_frame_t, const char *, const char *, int);
66 
67 static const struct frame_convert _convert_map_v2to3[] = {
68   { "BUF", "RBUF", NULL },
69   { "CNT", "PCNT", NULL },
70   { "COM", "COMM", NULL },
71   { "CRA", "AENC", NULL },
72   { "CRM", NULL,   NULL },
73   { "ETC", "ETCO", NULL },
74   { "EQU", "EQUA", NULL },
75   { "GEO", "GEOB", NULL },
76   { "IPL", "IPLS", NULL },
77   { "LNK", "LINK", convert_link },
78   { "MCI", "MCDI", NULL },
79   { "MLL", "MLLT", NULL },
80   { "PIC", "APIC", convert_apic },
81   { "POP", "POPM", NULL },
82   { "REV", "RVRB", NULL },
83   { "RVA", "RVAD", NULL },
84   { "SLT", "SYLT", NULL },
85   { "STC", "SYTC", NULL },
86   { "TAL", "TALB", NULL },
87   { "TBP", "TBPM", NULL },
88   { "TCM", "TCOM", NULL },
89   { "TCO", "TCON", NULL },
90   { "TCR", "TCOP", NULL },
91   { "TDA", "TDAT", NULL },
92   { "TDY", "TDLY", NULL },
93   { "TEN", "TENC", NULL },
94   { "TFT", "TFLT", NULL },
95   { "TIM", "TIME", NULL },
96   { "TKE", "TKEY", NULL },
97   { "TLA", "TLAN", NULL },
98   { "TLE", "TLEN", NULL },
99   { "TMT", "TMED", NULL },
100   { "TOA", "TOPE", NULL },
101   { "TOF", "TOFN", NULL },
102   { "TOL", "TOLY", NULL },
103   { "TOR", "TORY", NULL },
104   { "TOT", "TOAL", NULL },
105   { "TP1", "TPE1", NULL },
106   { "TP2", "TPE2", NULL },
107   { "TP3", "TPE3", NULL },
108   { "TP4", "TPE4", NULL },
109   { "TPA", "TPOS", NULL },
110   { "TPB", "TPUB", NULL },
111   { "TRC", "TSRC", NULL },
112   { "TRD", "TRDA", NULL },
113   { "TRK", "TRCK", NULL },
114   { "TSI", "TSIZ", NULL },
115   { "TSS", "TSSE", NULL },
116   { "TT1", "TIT1", NULL },
117   { "TT2", "TIT2", NULL },
118   { "TT3", "TIT3", NULL },
119   { "TXT", "TEXT", NULL },
120   { "TXX", "TXXX", NULL },
121   { "TYE", "TYER", NULL },
122   { "UFI", "UFID", NULL },
123   { "ULT", "USLT", NULL },
124   { "WAF", "WOAF", NULL },
125   { "WAR", "WOAR", NULL },
126   { "WAS", "WOAS", NULL },
127   { "WCM", "WCOM", NULL },
128   { "WCP", "WCOP", NULL },
129   { "WPB", "WPUB", NULL },
130   { "WXX", "WXXX", NULL },
131 
132   { "XRV", "XRVA", convert_rva },
133 
134   { NULL, NULL, NULL }
135 };
136 
137 static const struct frame_convert _convert_map_v3to4[] = {
138 
139   /* removed and changed frames in 2.4 */
140   { "EQUA", NULL, NULL },
141   { "IPLS", "TIPL", NULL },
142   { "RVAD", NULL, NULL },
143   { "TDAT", "TDRC", convert_time },
144   { "TIME", "TDRC", convert_time },
145   { "TORY", "TDOR", NULL },
146   /* TRDA is free-form, so we can't convert it to TDRC automatically */
147   { "TRDA", NULL, NULL },
148   { "TSIZ", NULL, NULL },
149   { "TYER", "TDRC", convert_time },
150 
151   { "XRVA", "RVA2", convert_rva },
152 
153   /* new frames in 2.4 */
154   { NULL, "ASPI", NULL },
155   { NULL, "EQU2", NULL },
156   { NULL, "RVA2", NULL },
157   { NULL, "SEEK", NULL },
158   { NULL, "SIGN", NULL },
159   { NULL, "TDEN", NULL },
160   { NULL, "TDOR", NULL },
161   { NULL, "TDRC", NULL },
162   { NULL, "TDRL", NULL },
163   { NULL, "TDTG", NULL },
164   { NULL, "TIPL", NULL },
165   { NULL, "TMCL", NULL },
166   { NULL, "TMOO", NULL },
167   { NULL, "TPRO", NULL },
168   { NULL, "TSOA", NULL },
169   { NULL, "TSOP", NULL },
170   { NULL, "TSOT", NULL },
171   { NULL, "TSST", NULL },
172 
173   /* frames whose format changed */
174   { "TCON", "TCON", convert_tcon },
175 
176   { NULL, NULL, NULL }
177 };
178 
179 /*** Start of converter routines ***/
180 
181 static int
convert_link(id3_frame_t f,const char * from,const char * to,int backward)182 convert_link(id3_frame_t f, const char *from, const char *to, int backward)
183 {
184   /* FIXME: implement, maybe */
185   return 0;
186 }
187 
188 struct imgtype_mimetype_struct {
189   const char *imgtype;
190   const char *mimetype;
191 };
192 
193 static const struct imgtype_mimetype_struct imgtype_mimetype_map[] = {
194   { "jpg", "jpeg" },
195   { "tif", "tiff" },
196   { "xbm", "x-xbitmap" },
197   { "xpm", "x-xpixmap" },
198   { "xwd", "x-xwindowdump" },
199   { "ras", "x-cmu-raster" },
200   { "pnm", "x-portable-anymap" },
201   { "pbm", "x-portable-bitmap" },
202   { "pgm", "x-portable-graymap" },
203   { "rgb", "x-rgb" },
204 
205   { NULL, NULL }
206 };
207 
208 /* convert v2.2 PIC to v2.3 APIC */
209 static int
_convert_apic_forward(id3_frame_t f)210 _convert_apic_forward(id3_frame_t f)
211 {
212   char imgtype[4];
213   char mimetype[32]; /* large enough for "image/" + largest string in map */
214   const struct imgtype_mimetype_struct *im_map;
215   unsigned char *data;
216   int i, newsz;
217 
218   data = id3_frame_get_raw(f);
219   if (data == NULL)
220     return 0;
221   memcpy(imgtype, data + 1, 3);
222   imgtype[3] = '\0';
223   for (i = 0; i < 3; i++)
224     imgtype[i] = tolower(imgtype[i]);
225 
226   /* use the map to get the corresponding mime type */
227   strcpy(mimetype, "image/");
228   im_map = imgtype_mimetype_map;
229   while (im_map->imgtype) {
230     if (strcmp(imgtype, im_map->imgtype) == 0) {
231       strcat(mimetype, im_map->mimetype);
232       break;
233     }
234     im_map++;
235   }
236 
237   /* just use image/<imgtype> if we didn't find it in the map */
238   if (im_map->imgtype == NULL)
239     strcat(mimetype, imgtype);
240 
241   i = strlen(mimetype);
242   newsz = f->sz - 3 + i + 1;
243   f->data = (unsigned char *)malloc(newsz);
244   if (f->data == NULL) {
245     f->data = data;
246     return -1;
247   }
248   f->data[0] = data[0];
249   strcpy((char *)f->data + 1, mimetype);
250   memcpy(f->data + 1 + i + 1, data + 4, f->sz - 4);
251   f->sz = newsz;
252   free(data);
253 
254   return 0;
255 }
256 
257 /* convert v2.3 APIC to v2.2 PIC */
258 static int
_convert_apic_backward(id3_frame_t f)259 _convert_apic_backward(id3_frame_t f)
260 {
261   char imgtype[4], *mimetype = NULL;
262   const struct imgtype_mimetype_struct *im_map;
263   unsigned char *data;
264   int i, newsz, len, bad_mimetype = 0;
265 
266   data = id3_frame_get_raw(f);
267   if (data == NULL)
268     return 0;
269 
270   /* make sure the mime type is terminated */
271   for (i = 1; i < f->sz; i++)
272     if (data[i] == '\0')
273       break;
274 
275   if (data[i] != '\0' || i < 8)
276     bad_mimetype = 1;
277 
278   if (!bad_mimetype) {
279     mimetype = (char *)data + 1;
280     len = strlen(mimetype);
281     for (i = 0; i < len; i++)
282       mimetype[i] = tolower(mimetype[i]);
283     if (strncmp(mimetype, "image/", 6) != 0)
284       bad_mimetype = 1;
285   }
286 
287   if (!bad_mimetype) {
288     mimetype += 6; /* skip "image/" */
289     /* use the map to get the corresponding image type */
290     im_map = imgtype_mimetype_map;
291     while (im_map->mimetype) {
292       if (strcmp(mimetype, im_map->mimetype) == 0) {
293 	strcpy(imgtype, im_map->imgtype);
294 	break;
295       }
296       im_map++;
297     }
298 
299     /* just use the first three chars after "image/"
300        if we didn't find it in the map */
301     if (im_map->mimetype == NULL)
302       strncpy(imgtype, mimetype, 3);
303     imgtype[3] = '\0';
304 
305   } else {
306     /* can't make head or tail of the mime type */
307     return 0;
308   }
309 
310   newsz = f->sz - len - 1 + 3;
311   memcpy(f->data + 1, imgtype, 3);
312   memmove(f->data + 4, f->data + 4 + len + 1, f->sz - 4 - len - 1);
313   f->sz = newsz;
314 
315   return 0;
316 }
317 
318 static int
convert_apic(id3_frame_t f,const char * from,const char * to,int backward)319 convert_apic(id3_frame_t f, const char *from, const char *to, int backward)
320 {
321   if (backward)
322     return _convert_apic_backward(f);
323   else
324     return _convert_apic_forward(f);
325 }
326 
327 /* used by the following convert_time() converter */
328 static int
_convert_time_backward(id3_frame_t f)329 _convert_time_backward(id3_frame_t f)
330 {
331   id3_t tag = f->id3;
332   id3_frame_t f2;
333   unsigned char *old_data;
334   unsigned char buf[32];
335   int len;
336 
337   /* split TDRC frame into TDAT, TIME, and TYER frames */
338   old_data = id3_frame_get_raw(f);
339   if (old_data == NULL)
340     return 0;
341   len = strlen((char *)old_data + 1);
342 
343   if (len >= 4) {
344     /* form TYER frame */
345     f2 = id3_frame_add(tag, "TYER");
346     if (f2 == NULL)
347       return -1;
348     if (id3_frame_set_raw(f2, old_data, 5) == -1)
349       return -1;
350   }
351 
352   if (len >= 10) {
353     /* form TDAT frame */
354     f2 = id3_frame_add(tag, "TDAT");
355     if (f2 == NULL)
356       return -1;
357     buf[0] = '\0';
358     buf[1] = old_data[9];
359     buf[2] = old_data[10];
360     buf[3] = old_data[6];
361     buf[4] = old_data[7];
362     if (id3_frame_set_raw(f2, buf, 5) == -1)
363       return -1;
364   }
365 
366   if (len >= 16) {
367     /* form TIME frame */
368     f2 = id3_frame_add(tag, "TIME");
369     if (f2 == NULL)
370       return -1;
371     buf[0] = '\0';
372     buf[1] = old_data[12];
373     buf[2] = old_data[13];
374     buf[3] = old_data[15];
375     buf[4] = old_data[16];
376     if (id3_frame_set_raw(f2, buf, 5) == -1)
377       return -1;
378   }
379 
380   return 1;
381 }
382 
383 /* used by the following convert_time() converter */
384 static int
_convert_time_forward(id3_frame_t f)385 _convert_time_forward(id3_frame_t f)
386 {
387   id3_t tag = f->id3;
388   id3_frame_t tdrc_f;
389   unsigned char *old_data;
390 
391   tdrc_f = id3_get_frame_by_id(tag, "TDRC");
392   if (tdrc_f == NULL) {
393     /* convert this frame to TDRC */
394     old_data = id3_frame_get_raw(f);
395     if (strcmp(f->id, "TDAT") == 0) {
396 
397       /* make sure the TIME frame is in the form DDMM */
398       if (strlen((char *)old_data + 1) != 4) {
399 	/* non-standard TDAT frame, so drop it */
400 	return 1;
401       }
402       f->sz = 11;
403       f->data = (unsigned char *)calloc(f->sz + 2, 1);
404       /* leave space for the year if we find a TYER later */
405       /* (string is split to avoid trigraphs) */
406       sprintf((char *)f->data + 1, "????" "-%c%c-%c%c",
407 	      old_data[3], old_data[4], old_data[1], old_data[2]);
408       free(old_data);
409 
410     } else if (strcmp(f->id, "TIME") == 0) {
411 
412       /* make sure the TIME frame is in the form HHmm */
413       if (strlen((char *)old_data + 1) != 4) {
414 	/* non-standard TIME frame, so drop it */
415 	return 1;
416       }
417       f->sz = 17;
418       f->data = (unsigned char *)calloc(f->sz + 2, 1);
419       /* leave space for the year and date for later TYER and TDAT */
420       /* (string is split to avoid trigraphs) */
421       sprintf((char *)f->data + 1, "????" "-??" "-??T%c%c:%c%c",
422 	      old_data[1], old_data[2], old_data[3], old_data[4]);
423       free(old_data);
424 
425     } else if (strcmp(f->id, "TYER") == 0) {
426       /* leave as it is */
427     } else {
428       /* unrecognized from field */
429       return -1;
430     }
431 
432   } else {
433 
434     /* There's already a TDRC frame, so we just add to it */
435     id3_frame_get_raw(f); /* make sure the data is read into f->data */
436     if (strcmp(f->id, "TDAT") == 0) {
437 
438       /* make sure the TIME frame is in the form DDMM */
439       if (strlen((char *)f->data + 1) != 4) {
440 	/* non-standard TDAT frame, so drop it */
441 	return 1;
442       }
443       /* make sure the TDRC frame can hold 11 bytes */
444       if (tdrc_f->sz < 11) {
445 	/* We need to make it bigger */
446 	old_data = id3_frame_get_raw(tdrc_f);
447 	tdrc_f->sz = 11;
448 	tdrc_f->data = (unsigned char *)realloc(tdrc_f->data, tdrc_f->sz + 2);
449 	if (tdrc_f->data == NULL) {
450 	  tdrc_f->data = old_data;
451 	  return -1;
452 	}
453 	tdrc_f->data[11] = tdrc_f->data[12] = '\0'; /* ensure termination */
454       }
455       /* data will now be YYYY-MM-DD, so splice in the -MM-DD */
456       sprintf((char *)tdrc_f->data + 5, "-%c%c-%c%c",
457 	      f->data[3], f->data[4], f->data[1], f->data[2]);
458 
459     } else if (strcmp(f->id, "TIME") == 0) {
460 
461       /* make sure the TIME frame is in the form HHmm */
462       if (strlen((char *)f->data + 1) != 4) {
463 	/* non-standard TIME frame, so drop it */
464 	return 1;
465       }
466       /* make sure the TDRC frame can hold 17 bytes */
467       if (tdrc_f->sz < 17) {
468 	/* We need to make it bigger */
469 	old_data = id3_frame_get_raw(tdrc_f);
470 	tdrc_f->sz = 17;
471 	tdrc_f->data = (unsigned char *)realloc(tdrc_f->data, tdrc_f->sz + 2);
472 	if (tdrc_f->data == NULL) {
473 	  tdrc_f->data = old_data;
474 	  return -1;
475 	}
476 	tdrc_f->data[17] = tdrc_f->data[18] = '\0'; /* ensure termination */
477       }
478       /* data will now be YYYY-MM-DDTHH:mm, so splice in the THH:mm */
479       sprintf((char *)tdrc_f->data + 11, "T%c%c:%c%c",
480 	      f->data[1], f->data[2], f->data[3], f->data[4]);
481 
482     } else if (strcmp(f->id, "TYER") == 0) {
483 
484       /* make sure the TYER frame is in the form YYYY */
485       if (strlen((char *)f->data + 1) != 4) {
486 	/* non-standard TYER frame, so drop it */
487 	return 1;
488       }
489       /* make sure the TDRC frame can hold 5 bytes */
490       if (tdrc_f->sz < 5) {
491 	/* We need to make it bigger */
492 	old_data = id3_frame_get_raw(tdrc_f);
493 	tdrc_f->sz = 5;
494 	tdrc_f->data = (unsigned char *)realloc(tdrc_f->data, tdrc_f->sz + 2);
495 	if (tdrc_f->data == NULL) {
496 	  tdrc_f->data = old_data;
497 	  return -1;
498 	}
499 	tdrc_f->data[5] = tdrc_f->data[6] = '\0'; /* ensure termination */
500       }
501       /* splice the YYYY into the beginning of the data */
502       memcpy(tdrc_f->data + 1, f->data + 1, 4);
503 
504     } else {
505       /* unrecognized from field */
506       return -1;
507     }
508 
509     return 1;
510   }
511 
512   return 0;
513 }
514 
515 /* This converter changes the v2.3 frames TDAT, TIME, and TYER
516  * to the v2.4 frame TDRC. */
517 static int
convert_time(id3_frame_t f,const char * from,const char * to,int backward)518 convert_time(id3_frame_t f, const char *from, const char *to, int backward)
519 {
520   if (backward)
521     return _convert_time_backward(f);
522   else
523     return _convert_time_forward(f);
524 }
525 
526 /* This converter changes between v2.3 and v2.4 TCON frame formats. */
527 static int
convert_tcon(id3_frame_t f,const char * from,const char * to,int backward)528 convert_tcon(id3_frame_t f, const char *from, const char *to, int backward)
529 {
530   char *src, *dest, *rparen;
531   unsigned char *data;
532   char *endptr;
533   int i, newsz, len, last_was_nonnumeric;
534   long gnum;
535 
536   data = id3_frame_get_raw(f);
537 
538   if (f->sz < 1)
539     return 0;
540 
541   /* FIXME: need to test backwards -- fix mp3tag.c */
542   if (backward) {
543 
544     /* "129" 0x00 "9" 0x00 ==> "(129)(9)" */
545     /* "129" 0x00 "(I think)" 0x00 ==> "(129)((I think)" */
546 
547     /* frame can grow, so allocate new data pointer */
548     newsz = f->sz + 2;
549     for (i = 0; i < f->sz; i++) {
550       if (data[i] == '(' || data[i] == '\0')
551 	newsz++;
552     }
553     f->data = (unsigned char *)calloc(newsz, 1);
554     if (f->data == NULL) {
555       f->data = data;
556       return -1;
557     }
558 
559     src = (char *)data;
560     dest = (char *)f->data;
561     *dest++ = *src++; /* text encoding byte */
562     last_was_nonnumeric = 0;
563     while (src - (char *)data < f->sz) {
564 
565       /* Check for a numeric string between 0 and 255, by itself */
566       if (src[0] >= '0' && src[0] <= '9') {
567 	gnum = strtol(src, &endptr, 10);
568 	if (*endptr == '\0' && gnum >= 0 && gnum <= 255) {
569 	  dest += sprintf(dest, "(%d)", (int)gnum);
570 	  src = endptr + 1;
571 	  last_was_nonnumeric = 0;
572 	  continue;
573 	}
574       }
575 
576       /* It's a non-numeric string, so escape the lparens and copy */
577       if (last_was_nonnumeric) {
578 	/* separate consecutive non-numeric strings by '/' */
579 	*dest++ = '/';
580       }
581       while (*src != '\0') {
582 	if (*src == '(')
583 	  *dest++ = '(';
584 	*dest++ = *src++;
585       }
586       src++; /* skip the '\0' */
587       last_was_nonnumeric = 1;
588     }
589     newsz = dest - (char *)f->data;
590     free(data);
591 
592   } else {
593 
594     /* "(129)(9)" ==> "129" 0x00 "9" 0x00 */
595     /* "(129)((I think)" ==> "129" 0x00 "(I think)" 0x00 */
596 
597     src = dest = (char *)data + 1;
598     while (src - (char *)data < f->sz) {
599 
600       if (src[0] == '(') {
601 
602 	rparen = strchr(src, ')');
603 	if (src[1] == '(') {
604 	  /* if "((" is encountered, drop one '(' */
605 	  src++;
606 	  if (rparen)
607 	    len = rparen - src + 1;
608 	  else
609 	    len = f->sz - (src - (char *)data);
610 	  memmove(dest, src, len);
611 	  src += len;
612 	  dest += len;
613 	} else {
614 	  /* found start of a field */
615 	  if (rparen == NULL)
616 	    break;
617 	  *rparen = '\0';
618 	  if (dest[-1] == '\0')
619 	    src++;
620 	  else
621 	    *src = '\0';
622 	  len = rparen - src;
623 	  memmove(dest, src, len);
624 	  src += len;
625 	  dest += len;
626 	}
627 
628       } else {
629 	*dest++ = *src++;
630       }
631     }
632 
633     newsz = dest - (char *)data;
634   }
635 
636   f->sz = newsz;
637   f->data[f->sz] = '\0';
638 
639   return 0;
640 }
641 
642 /*
643  * We use experimental tags XRV and XRVA in v2.2 and v2.3 tags, since
644  * they don't have useful native relative volume adjust frames like
645  * RVA2. Their format is the same as RVA2, so convert_rva() doesn't
646  * actually do any conversion; it's just to ensure that the format is
647  * correct, since someone else could also be putting XRVA frames with
648  * entirely different data in tags.  If we don't recogize the format,
649  * we just drop the tag.
650  */
651 static int
convert_rva(id3_frame_t f,const char * from,const char * to,int backward)652 convert_rva(id3_frame_t f, const char *from, const char *to, int backward)
653 {
654   int i, peakbytes;
655   unsigned char *data;
656 
657   data = id3_frame_get_raw(f);
658 
659   /* check identification string */
660   for (i = 0; i < f->sz; i++)
661     if (data[i] == '\0')
662       break;
663   if (data[i] != '\0')
664     return 1;
665 
666   i++;
667   while (1) {
668     /* check channel type */
669     if (i >= f->sz || data[i] > 0x08)
670       return 1;
671 
672     /* check peak data */
673     i += 3;
674     if (i >= f->sz)
675       return 1;
676     peakbytes = data[i] / 8;
677     i += peakbytes;
678     if (i >= f->sz)
679       return 1;
680 
681     /* end of field; it's okay to end here */
682     i++;
683     if (i >= f->sz)
684       break;
685   }
686 
687   return 0;
688 }
689 
690 /*** End of converter routines ***/
691 
692 static const struct frame_convert *
find_converter(const struct frame_convert * map,const char * from,int backw)693 find_converter(const struct frame_convert *map, const char *from, int backw)
694 {
695   const char *s;
696 
697   while (map->from || map->to) {
698     s = backw ? map->to : map->from;
699     if (s && memcmp(from, s, 4) == 0)
700       return map;
701     map++;
702   }
703   return NULL;
704 }
705 
706 static int
convert_frame(id3_frame_t f,int from_vers,int to_vers)707 convert_frame(id3_frame_t f, int from_vers, int to_vers)
708 {
709   const struct frame_convert *fc_map = NULL;
710   int ret, backward = 0;
711   const char *newid;
712 
713   if (from_vers == to_vers)
714     return 0;
715 
716   if (from_vers - to_vers > 1) {
717     ret = convert_frame(f, from_vers, to_vers + 1);
718     if (ret == -1)
719       return -1;
720     from_vers = to_vers + 1;
721   } else if (to_vers - from_vers > 1) {
722     ret = convert_frame(f, from_vers, to_vers - 1);
723     if (ret == -1)
724       return -1;
725     from_vers = to_vers - 1;
726   }
727 
728   /* to and from are always one version apart at this point */
729   switch (from_vers) {
730   case 2:
731     /* we can only be converting to v3 */
732     fc_map = _convert_map_v2to3;
733     backward = 0;
734     break;
735   case 3:
736     switch (to_vers) {
737     case 2:
738       fc_map = _convert_map_v2to3;
739       backward = 1;
740       break;
741     case 4:
742       fc_map = _convert_map_v3to4;
743       backward = 0;
744       break;
745     }
746     break;
747   case 4:
748     /* we can only be converting to v3 */
749     fc_map = _convert_map_v3to4;
750     backward = 1;
751     break;
752   }
753 
754   fc_map = find_converter(fc_map, f->id, backward);
755   if (fc_map) {
756     if (fc_map->converter) {
757       ret = fc_map->converter(f, fc_map->from, fc_map->to, backward);
758       if (ret == -1)
759 	return -1;
760       if (ret == 1) {
761 	id3_frame_delete(f);
762 	return 0;
763       }
764     }
765     newid = backward ? fc_map->from : fc_map->to;
766     if (newid) {
767       strcpy(f->id, newid);
768     } else {
769       /* newid is NULL, so the frame should just be dropped */
770       id3_frame_delete(f);
771     }
772   }
773 
774   return 0;
775 }
776 
777 int
id3_set_version(id3_t tag,enum id3_version ver)778 id3_set_version(id3_t tag, enum id3_version ver)
779 {
780   int oldversion;
781   id3_frame_t fr, nextfr;
782 
783   /* make sure headers have been read */
784   if (id3_frame_count(tag) == -1)
785     return -1;
786 
787   /* set version field */
788   oldversion = tag->version;
789   switch (ver) {
790   case ID3_VERSION_2_3: tag->version = 3; break;
791   case ID3_VERSION_2_4: tag->version = 4; break;
792   case ID3_VERSION_2_2:
793     /* we choose not to write ID3v2.2 tags, so fall through */
794   default:
795     errno = EINVAL;
796     return -1;
797   }
798 
799   if (tag->version == oldversion)
800     return 0; /* nothing to do */
801   if (oldversion < 2 || oldversion > 4) {
802     /* we only know how to convert from v2.2, v2.3, and v2.4 */
803     errno = EINVAL;
804     return -1;
805   }
806 
807   /* convert frames */
808   fr = tag->frame_hd;
809   while (fr) {
810     /* fr may get removed in the conversion process, so grab the next
811        frame before converting */
812     nextfr = fr->next;
813     if (convert_frame(fr, oldversion, tag->version) == -1)
814       return -1;
815     fr = nextfr;
816   }
817 
818   return 0;
819 }
820 
821 enum id3_version
id3_get_version(id3_t tag)822 id3_get_version(id3_t tag)
823 {
824   if (id3_get_size(tag) == -1)
825     return ID3_VERSION_NONE;
826   switch (tag->version) {
827   case 2: return ID3_VERSION_2_2;
828   case 3: return ID3_VERSION_2_3;
829   case 4: return ID3_VERSION_2_4;
830   }
831   return ID3_VERSION_NONE;
832 }
833