~ft/libtags

1a9591bd4f932a6ca2fba0eb84cb6b790c8c7250 — Sigrid Solveig Haflínudóttir 2 months ago 78aa236 1.1
vorbis: support METADATA_BLOCK_PICTURE; don't stop on tags too large, truncate and skip instead
4 files changed, 149 insertions(+), 7 deletions(-)

A base64.c
M meson.build
M tagspriv.h
M vorbis.c
A base64.c => base64.c +67 -0
@@ 0,0 1,67 @@
#include "tagspriv.h"

static const uint8_t d[] = {
	66,66,66,66,66,66,66,66,66,66,64,66,66,66,66,66,66,66,66,66,66,66,66,66,66,
	66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,62,66,66,66,63,52,53,
	54,55,56,57,58,59,60,61,66,66,66,65,66,66,66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
	10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,66,66,66,66,66,66,26,27,28,
	29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,66,66,
	66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,
	66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,
	66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,
	66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,
	66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,
	66,66,66,66,66,66
};
enum {
	B64_WHITESPACE = 64,
	B64_EQUALS,
	B64_INVALID,
};

int
debase64(uint8_t *in, int insz, uint8_t *out, int outsz)
{
	uint8_t *end = in + insz;
	uint8_t iter = 0;
	uint32_t buf = 0;
	int len = 0;

	while(in < end){
		uint8_t c = d[*in++];

		switch(c){
		case B64_WHITESPACE:
			continue;
		case B64_INVALID:
			return -1;
		case B64_EQUALS:
			in = end;
			continue;
		default:
			buf = buf << 6 | c;
			if(++iter == 4){
				if((len += 3) > outsz)
					return -1;
				*out++ = (buf >> 16) & 0xff;
				*out++ = (buf >> 8) & 0xff;
				*out++ = buf & 0xff;
				buf = 0;
				iter = 0;
			}
		}
	}

	if(iter == 3){
		if((len += 2) > outsz)
			return -1;
		*out++ = (buf >> 10) & 0xff;
		*out++ = (buf >> 2) & 0xff;
	}else if(iter == 2){
		if(++len > outsz)
			return -1;
		*out++ = (buf >> 4) & 0xff;
	}

	return len;
}

M meson.build => meson.build +1 -0
@@ 27,6 27,7 @@ endif
src_lib = [
	'437.c',
	'8859.c',
	'base64.c',
	'flac.c',
	'id3genres.c',
	'id3v1.c',

M tagspriv.h => tagspriv.h +10 -0
@@ 57,6 57,16 @@ int cp437toutf8(uint8_t *o, int osz, const uint8_t *s, int sz);
 */
void cbvorbiscomment(Tagctx *ctx, char *k, char *v);

/*
 * Used to decode base64-encoded picture block.
 */
int debase64(uint8_t *in, int insz, uint8_t *out, int outsz);

/*
 * METADATA_BLOCK_PICTURE reader function.
 */
int mbpdec(void *buf, int *cnt);

void tagscallcb(Tagctx *ctx, int type, const char *k, char *s, int offset, int size, Tagread f);

#define txtcb(ctx, type, k, s) tagscallcb(ctx, type, k, (char*)s, 0, 0, nil)

M vorbis.c => vorbis.c +71 -7
@@ 27,7 27,8 @@ static const struct {
};

void
cbvorbiscomment(Tagctx *ctx, char *k, char *v){
cbvorbiscomment(Tagctx *ctx, char *k, char *v)
{
	int i;

	if(*v == 0)


@@ 43,11 44,44 @@ cbvorbiscomment(Tagctx *ctx, char *k, char *v){
}

int
mbpdec(void *buf, int *cnt)
{
	int sz, n;
	uint8_t *v;

	v = buf;
	if((n = debase64(v, *cnt, v, *cnt)) <= 0)
		return -1;

	beuint(v); /* id3v2 APIC type */
	v += 4; n -= 4;
	sz = beuint(v); /* mime size */
	v += 4; n -= 4;
	if(sz < 0 || sz >= n-4-4-4-4-4-4)
		return -1;
	v += sz; n -= sz; /* skip MIME */
	sz = beuint(v); /* description size */
	v += 4; n -= 4;
	if(sz < 0 || sz >= n-4-4-4-4-4)
		return -1;
	v += sz; n -= sz; /* skip description */
	v += 4+4+4+4; n -= 4+4+4+4; /* skip width, height, depth, palette info */
	sz = beuint(v); /* picture size */
	v += 4; n -= 4;
	if(sz <= 0 || sz > n)
		return -1;
	memmove(buf, v, sz);
	*cnt = sz;

	return 0;
}

int
tagvorbis(Tagctx *ctx)
{
	char *v;
	char *v, *mime;
	uint8_t *d, h[4];
	int sz, numtags, i, npages, pgend;
	int sz, picsz, numtags, i, npages, pgend, skip, off, n;

	d = (uint8_t*)ctx->buf;
	/* need to find vorbis frame with type=3 */


@@ 99,11 133,12 @@ tagvorbis(Tagctx *ctx)
			if(pgend < ctx->seek(ctx, 0, 1)+sz)
				break;

			skip = 0;
			if(sz > ctx->bufsz-1){
				if(ctx->seek(ctx, sz, 1) < 0)
					return -1;
				continue;
				skip = sz - (ctx->bufsz-1);
				sz -= skip;
			}

			if(ctx->read(ctx, ctx->buf, sz) != sz)
				return -1;
			ctx->buf[sz] = 0;


@@ 111,7 146,36 @@ tagvorbis(Tagctx *ctx)
			if((v = strchr(ctx->buf, '=')) == nil)
				return -1;
			*v++ = 0;
			cbvorbiscomment(ctx, ctx->buf, v);
			if(strcasecmp(ctx->buf, "metadata_block_picture") != 0)
				cbvorbiscomment(ctx, ctx->buf, v);
			else{
				/* off and picsz will point at the base64-encoded picture block */
				off = ctx->seek(ctx, 0, 1) - sz + (v - ctx->buf);
				picsz = sz + skip - (v - ctx->buf);
				n = sz - (v - ctx->buf); /* at most this amount is available */
				n &= ~3; /* modulo 4 sextets, so debase64 gets complete bytes */
				n = debase64((uint8_t*)v, n, (uint8_t*)ctx->buf, ctx->bufsz);
				/* https://xiph.org/flac/format.html#metadata_block_picture */
				if(n > 4+4+0+4+0+4+4+4+4+4+0){
					v = ctx->buf;
					beuint(v); /* id3v2 APIC type */
					v += 4; n -= 4;
					sz = beuint(v); /* mime size */
					v += 4; n -= 4;
					if(sz < 0 || sz >= n-4-4-4-4-4-4)
						return -1;
					mime = v;
					v += sz; n -= sz; /* skip MIME */
					sz = beuint(v); /* description size */
					v += 4; n -= 4;
					if(sz < 0 || sz >= n-4-4-4-4-4)
						return -1;
					*v = 0; /* null-terminate MIME */
					tagscallcb(ctx, Timage, "", mime, off, picsz, mbpdec);
				}
			}
			if(ctx->seek(ctx, skip, 1) < 0)
				return -1;
		}
	}