1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <errno.h>
6 #include <limits.h>
7 #include "fileops.h"
8 #include "id3v2.h"
9 
10 /*
11 
12   copyright (c) 2003-2006, 2015 squell <squell@alumina.nl>
13 
14   use, modification, copying and distribution of this software is permitted
15   under the conditions described in the file 'COPYING'.
16 
17   NOTE! Unsynchronization is only supported for reading
18 
19 */
20 
21 /* some programs write evil tags. it's probably nice to see failure modes */
22 #ifdef ID3v2_DEBUG
23 #   define refuse(label, msg, info) { \
24         fprintf(stderr, "%s: id3v2 "msg"\n", fname, info); \
25         goto label; \
26     }
27 #else
28 #   define refuse(label, msg, info) goto label;
29 #endif
30 
31 /* fix bugs introduced by other programs? */
32 #define ID3v2_FIX 1
33 
34 typedef unsigned long ulong;
35 typedef unsigned char uchar;
36 
37 enum ID3_hdr_flag {
38     UNSYNC = 0x80,
39     XTND   = 0x40,      /* 2.3; in 2.2: unused compression bit */
40     XPER   = 0x20,
41     FOOTER = 0x10       /* 2.4 */
42 };
43 
44 enum ID3_frm_flag1 {
45     TAP    = 0x80,      /* 2.3, in 2.4: shifted 1 to the right */
46     FAP    = 0x40,
47     RO     = 0x20
48 };
49 
50 enum ID3_frm_flag2 {
51     PACK   = 0x80,      /* 2.3 */
52     ENC    = 0x40,
53     GRP    = 0x20,
54 
55     GRP4   = 0x40,      /* 2.4 */
56     PACK4  = 0x08,
57     ENC4   = 0x04,
58     UNSYNC4= 0x02,
59     DLI4   = 0x01
60 };
61 
62 struct raw_hdr {
63     uchar ID   [3];
64     uchar ver;
65     uchar rev;
66     uchar flags;
67     uchar size [4];
68 };
69 
70 union raw_frm {
71     uchar ID[4];
72     struct raw_frm_2 {
73         uchar ID   [3];
74         uchar size [3];
75     } v2;
76     struct raw_frm_3 {
77         uchar ID   [4];
78         uchar size [4];
79         uchar flags[2];
80     } v3;
81 };
82 
83 typedef int raw_hdr_size_check [sizeof(struct raw_hdr)==10 ? 1 : -1];
84 typedef int raw_frm_size_check [sizeof(union  raw_frm)==10 ? 1 : -1];
85 
86 /* ==================================================== */
87 
88  /* en-/de- unsynchronizing */
89 
unsync_dec(uchar * dst,uchar * src,ulong size)90 static uchar *unsync_dec(uchar *dst, uchar *src, ulong size)
91 {
92     while(size--)
93         if( (*dst++ = *src++) == 0xFF && size > 0 ) {
94             size--;
95             if( (*dst = *src++) != 0x00 ) dst++;
96         }
97     return dst;
98 }
99 
100 /* ==================================================== */
101 
ul4(uchar n[4])102 static ulong ul4(uchar n[4])
103 {
104     return (ulong)n[0]<<24
105          | (ulong)n[1]<<16
106          | (ulong)n[2]<< 8
107          | (ulong)n[3]<< 0;
108 }
109 
nbo4(uchar h[4],ulong n)110 static void nbo4(uchar h[4], ulong n)
111 {
112     h[0] = (n >> 24) & 0xFF;
113     h[1] = (n >> 16) & 0xFF;
114     h[2] = (n >>  8) & 0xFF;
115     h[3] = (n      ) & 0xFF;
116 }
117 
ul4ss(uchar h[4])118 static ulong ul4ss(uchar h[4])                            /* "synch safe" */
119 {
120     return (ulong)(h[0] & 0x7F) << 21
121          | (ulong)(h[1] & 0x7F) << 14
122          | (ulong)(h[2] & 0x7F) <<  7
123          | (ulong)(h[3] & 0x7F);
124 }
125 
nbo4ss(uchar h[4],ulong n)126 static void nbo4ss(uchar h[4], ulong n)
127 {
128     h[0] = (n >> 21) & 0x7F;
129     h[1] = (n >> 14) & 0x7F;
130     h[2] = (n >>  7) & 0x7F;
131     h[3] = (n      ) & 0x7F;
132 }
133 
checkid(const char * ID,size_t n)134 static int checkid(const char *ID, size_t n)    /* check ID for A..Z0..9 */
135 {
136     while(n--) {
137         if( !isupper(*ID) && !isdigit(*ID) ) return 0;
138         ++ID;
139     }
140     return 1;
141 }
142 
calcsize(uchar * buf,ulong max)143 static long calcsize(uchar *buf, ulong max)
144 {
145     union raw_frm *frame;
146     ulong size = 0;
147     ulong step;
148     int version = buf[-1];
149     int ID_siz = 3+(version>2);
150 
151     while(size < max && checkid((char*)buf, ID_siz)) {
152         frame = (union raw_frm*)buf;
153         switch(version) {
154             case  2: step = sizeof(frame->v2) + (ul4(frame->v2.size) >> 8); break;
155             case  3: step = sizeof(frame->v3) + ul4(frame->v3.size);        break;
156             case  4: step = sizeof(frame->v3) + ul4ss(frame->v3.size);      break;
157             default: return -1;
158         }
159         if(size+step <= size) return -1;
160         size += step;
161         buf  += step;
162     }
163     return size<=max? (long)size : -1;
164 }
165 
166 /* in v2.4, unsync is per-frame for not adequately explained reasons.
167    this function requires the actual size as determined by calcsize() */
168 
unsync_frames_v2_4(uchar * buf,ulong size)169 static ulong unsync_frames_v2_4(uchar *buf, ulong size)
170 {
171     uchar *end = buf+size;
172     uchar *out = buf;
173 
174     while(buf < end) {
175         union raw_frm *frame = (union raw_frm*)buf;
176         ulong step = sizeof(frame->v3) + ul4ss(frame->v3.size);
177         if( frame->v3.flags[1] & UNSYNC4 ) {
178             frame = (union raw_frm*)out;
179             out = unsync_dec(out, buf, step);
180             /* update frame size & clear UNSYNC4 bit */
181             nbo4ss(frame->v3.size, out-frame->ID - sizeof(*frame));
182             frame->v3.flags[1] &= ~UNSYNC4;
183         } else {
184             out = (uchar*)memmove(out, buf, step) + step;
185         }
186         buf += step;
187     }
188     memset(out, 0, buf-out);
189     return out - (end-size);
190 }
191 
192 /* ==================================================== */
193 
ID3_readf(const char * fname,size_t * tagsize)194 void *ID3_readf(const char *fname, size_t *tagsize)
195 {
196     struct raw_hdr rh;
197     uchar *buf;
198     long pad, size = 0;
199 
200     FILE *f = fopen(fname, "rb");
201 
202     if( !f )
203         refuse(abort, "could not open", 0);
204 
205     if( fread(&rh, sizeof(struct raw_hdr), 1, f) != 1 )
206         refuse(abort_file, "file too small", 0);              /* IO error */
207 
208     if( memcmp(rh.ID, "ID3", 3) != 0 )                /* not an ID3v2 tag */
209         refuse(abort_file, "contains no ID3 identifier", 0);
210 
211     if( rh.ver < 2 || rh.ver > 4 )                     /* unknown version */
212         refuse(abort_file, "unsupported ID3v2.%d", rh.ver);
213 
214     size = ul4ss(rh.size);
215 
216     buf = malloc(size+1+4);                       /* over-alloc 4+1 chars */
217     if(!buf)                                          /* ohhhhhhh.. crap. */
218         refuse(abort_file, "could not allocate tag (%ld bytes)", size);
219 
220     (++buf)[-1] = rh.ver;                         /* prepend version byte */
221     buf[size] = 0;        /* make sure we have a pseudoframe to terminate */
222 
223     if( fread(buf,1,size,f) != size )
224         refuse(abort_mem, "could not read tag from file (%ld bytes)", size);
225 
226     if( rh.flags & UNSYNC && rh.ver <= 3 )        /* unsync on entire tag */
227         size = unsync_dec(buf, buf, size) - buf;
228 
229     if( rh.flags & XTND ) {                 /* get rid of extended header */
230         ulong xsiz = (rh.ver==3?ul4:ul4ss)(buf) + 4;
231         if(xsiz < 4 || xsiz >= size)
232             refuse(abort_mem, "extended header incorrect (%ld bytes)", xsiz);
233         size -= xsiz;                                   /* but try anyway */
234         memmove(&buf[0], &buf[xsiz], size);
235     }
236 
237     pad  = size;                                /* check semantics of tag */
238     size = calcsize(buf, size);
239 
240     if( rh.ver == 4 && size > 0 )  /* v2.4: just ignore the global UNSYNC */
241         size = unsync_frames_v2_4(buf, size);
242 
243     if(tagsize) *tagsize = size;
244 
245     if(size < 0)                                 /* semantic error in tag */
246         refuse(abort_mem, "tag larger than reported size (%ld bytes)", pad);
247 
248     while(size < pad)
249         if( buf[size++] == 0xff ) {                   /* padding not zero */
250             if(size-1 == pad-sizeof(struct raw_hdr) && ID3v2_FIX)
251                 ;           /* tag contains a rare bug; make an exception */
252             else
253                 refuse(abort_mem, "padding contains framesync (%02x)", buf[size-1]);
254         }
255 
256     ;                       /* nothing required to handle ID3v2.4 footers */
257 
258     fclose(f);
259     return --buf;
260 
261 abort_mem:                     /* de-alloc, close file and return failure */
262     free(--buf);
263 abort_file:                              /* close file and return failure */
264     fclose(f);
265 abort:
266     if(tagsize) *tagsize = size;
267     return 0;
268 }
269 
270 #ifndef ID3v2_READONLY
271 
_wfail(const char * srcname,const char * dstname)272 static void _wfail(const char *srcname, const char *dstname)
273 {
274     fprintf(stderr, "%s -> %s: %s\n", srcname, dstname, strerror(errno));
275     exit(255);                                              /* sayonara! */
276 }
277 
278 void (*ID3_wfail)(const char *srcname, const char *dstname) = _wfail;
279 
ID3_writef(const char * fname,const void * buf,size_t reqsize)280 int ID3_writef(const char *fname, const void *buf, size_t reqsize)
281 {
282     struct raw_hdr new_h = { "ID3", 0, 0, 0, { 0, } };
283     struct raw_hdr rh    = { { 0 } };                       /* duct tape */
284     uchar* src;
285     long size = 0;
286 
287     FILE *f = fopen(fname, "rb+");
288     if(!f) return 0;
289 
290     if(buf) {
291         src  = (uchar*)buf + 1;
292         size = calcsize(src, LONG_MAX);
293         new_h.ver = src[-1];
294     }
295 
296     if(size < 0)
297         goto abort;                                   /* error in caller */
298 
299     if( fread(&rh, sizeof(struct raw_hdr), 1, f) && memcmp(rh.ID, "ID3", 3) == 0 ) {
300         long orig;                                   /* allready tagged? */
301 
302         if( rh.ver < 2 || rh.ver > 4 )
303             goto abort;                           /* handles ID3v2.[234] */
304 
305         orig = ul4ss(rh.size);
306 
307         if( fseek(f, orig, SEEK_CUR) != 0 )
308             goto abort;
309 
310         if( ID3v2_FIX && fseek(f, -10, SEEK_CUR) == 0 ) {
311             if(ungetc(getc(f), f) == 0xFF)        /* fix off-by-10 error */
312                 orig -= 10;
313             else
314                 if( fseek(f, 10, SEEK_CUR) != 0 ) goto abort;
315         }
316 
317         if( size>0 && size<=orig && !reqsize) { /* enough reserved space */
318             nbo4ss(new_h.size, orig);
319             rewind(f);
320             fwrite(&new_h, sizeof new_h, 1, f);
321             fwrite(src, size, 1, f);
322             fpadd(0, orig-size, f);
323             if(ferror(f) | fclose(f)) {         /* abandon all hope,     */
324                 ID3_wfail(fname, fname);            /* ye who enter here */
325                 return 0;
326             }
327             return 1;
328         }
329     } else {
330         if(size == 0) {
331             fclose(f);
332             return 1;
333         }
334         rewind(f);
335     }
336                                                         /* file rewriter */
337     {
338         ulong nsize = ((size+sizeof new_h+0x1FF) & ~0x1FF) - sizeof new_h;
339         int ok;                                      /* rnd to 512 bytes */
340 
341         char *tmp;
342         FILE *nf = opentemp(fname, &tmp);
343 
344         if( reqsize ) {
345             reqsize = (reqsize < sizeof new_h)? 0 : reqsize - sizeof new_h;
346             nsize   = (size < reqsize)? reqsize : size;
347         }
348 
349         if( !nf )
350             goto abort;
351 
352         if(size != 0) {
353             nbo4ss(new_h.size, nsize);
354             ok = fwrite(&new_h, 1, sizeof new_h, nf) == sizeof new_h
355               && fwrite(src, 1, size, nf)            == size
356               && fpadd(0, nsize-size, nf)            == nsize-size
357               && fcopy(nf, f);
358         } else {
359             ok = fcopy(nf, f);                  /* remove ID3v2 tag only */
360         }
361         fclose(f);
362         ok = fclose(nf) == 0 && ok;
363 
364         if(ok) {
365             ok = mvfile(tmp, fname);
366             if(!ok)
367                 ID3_wfail(tmp, fname);                 /* grievous error */
368         } else {
369             remove(tmp);                                      /* failure */
370         }
371         free(tmp);
372         return ok;
373     }
374 
375 abort:                                  /* close file and return failure */
376     fclose(f);
377     return 0;
378 }
379 
380 #endif
381 
ID3_free(const void * buf)382 void ID3_free(const void *buf)
383 {
384     free((void*)buf);
385 }
386 
387 /* ==================================================== */
388 
389 static const size_t raw_frm_sizeof[2]
390   = { sizeof(struct raw_frm_2), sizeof(struct raw_frm_3) };
391 
ID3_start(ID3FRAME f,const void * buf)392 ID3VER ID3_start(ID3FRAME f, const void *buf)
393 {
394     register uchar ver = *(uchar*)buf;
395     f->_rev  = ver-2;
396     f->ID[3] = f->ID[4] = 0;
397     f->data  = (char*)buf + 1;
398     f->size  = 0;
399 
400     f->tag_volit  =                      /* set highly useful data */
401     f->file_volit =
402     f->readonly   =
403     f->packed     =
404     f->encrypted  =
405     f->grouped    = 0;
406 
407     return ver>=2 && ver<=4? 2+(ver>2) : 0; /* pretend v2.4 = v2.3 */
408 }
409 
ID3_frame(ID3FRAME f)410 int ID3_frame(ID3FRAME f)
411 {
412     union raw_frm *frame = (union raw_frm*)(f->data + f->size);
413     int version = f->_rev+2;
414     int ID_siz = 3+(version>2);
415 
416     f->data += f->size + raw_frm_sizeof[version>2];
417 
418     memcpy(f->ID, frame->ID, ID_siz);
419 
420     if(version==3) {                                    /* ID3v2.3 stuff */
421         f->size       = ul4(frame->v3.size);          /* copy essentials */
422         f->tag_volit  = !!( frame->v3.flags[0] & TAP  );
423         f->file_volit = !!( frame->v3.flags[0] & FAP  );
424         f->readonly   = !!( frame->v3.flags[0] & RO   );
425 
426         f->packed     = !!( frame->v3.flags[1] & PACK );
427         f->encrypted  = !!( frame->v3.flags[1] & ENC  );
428         f->grouped    = !!( frame->v3.flags[1] & GRP  );
429     } else if(version==4) {
430         f->size       = ul4ss(frame->v3.size);
431         f->tag_volit  = !!( frame->v3.flags[0]>>1 & TAP  );
432         f->file_volit = !!( frame->v3.flags[0]>>1 & FAP  );
433         f->readonly   = !!( frame->v3.flags[0]>>1 & RO   );
434 
435         f->packed     = !!( frame->v3.flags[1] & PACK4 );
436         f->encrypted  = !!( frame->v3.flags[1] & ENC4  );
437         f->grouped    = !!( frame->v3.flags[1] & GRP4  );
438 
439         if( frame->v3.flags[1] & DLI4 )          /* id3v2.4 crufty stuff */
440             f->data += 4, f->size -= 4;
441     } else {
442         f->size       = ul4(frame->v2.size) >> 8;
443     }
444 
445     return checkid(f->ID, ID_siz);
446 }
447 
448 /* ==================================================== */
449 
450 #ifndef ID3v2_READONLY
451 
ID3_put(void * dest,ID3VER version,const char ID[4],const void * src,size_t len)452 void *ID3_put(void *dest, ID3VER version, const char ID[4], const void *src, size_t len)
453 {
454     union raw_frm *frame = (union raw_frm*)dest;
455     uchar *cdest         = dest;
456 
457     if(!ID) {
458         (++cdest)[-1] = version;                         /* initialize */
459         cdest[0]      = 0;
460         return cdest;
461     } else if((version|1) != 3 || !checkid(ID, version+1) || ID[version+1]) {
462         return cdest;
463     }
464 
465     memcpy(frame->ID, ID, version+1);
466     if(version == 3) {                                   /* ID3v2.3 stuff */
467         nbo4(frame->v3.size, len);
468         frame->v3.flags[0] = 0;
469         frame->v3.flags[1] = 0;
470     } else {
471         nbo4(frame->v2.size, len << 8);      /* extra byte doesn't matter */
472     }
473 
474     cdest = memcpy(cdest + raw_frm_sizeof[version==3], src, len);
475     cdest[len] = 0;                                        /* suffixing 0 */
476     return cdest + len;
477 }
478 
479 #endif
480