/* slight modification: no 'filename' arg, and errors are passed back in 'violation' instead of printed to stderr */
static FLAC__bool set_vc_field(FLAC__StreamMetadata *block, const Argument_VcField *field, FLAC__bool *needs_write, FLAC__bool raw, const char **violation)
{
	FLAC__StreamMetadata_VorbisComment_Entry entry;
	char *converted;

	FLAC__ASSERT(0 != block);
	FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
	FLAC__ASSERT(0 != field);
	FLAC__ASSERT(0 != needs_write);

	if(field->field_value_from_file) {
		/* read the file into 'data' */
		FILE *f = 0;
		char *data = 0;
		const off_t size = grabbag__file_get_filesize(field->field_value);
		if(size < 0) {
			*violation = "can't open file for tag value";
			return false;
		}
		if(size >= 0x100000) { /* magic arbitrary limit, actual format limit is near 16MB */
			*violation = "file for tag value is too large";
			return false;
		}
		if(0 == (data = malloc(size+1)))
			die("out of memory allocating tag value");
		data[size] = '\0';
		if(0 == (f = fopen(field->field_value, "rb")) || fread(data, 1, size, f) != (size_t)size) {
			free(data);
			if(f)
				fclose(f);
			*violation = "error while reading file for tag value";
			return false;
		}
		fclose(f);
		if(strlen(data) != (size_t)size) {
			free(data);
			*violation = "file for tag value has embedded NULs";
			return false;
		}

		/* move 'data' into 'converted', converting to UTF-8 if necessary */
		if(raw) {
			converted = data;
		}
		else if(utf8_encode(data, &converted) >= 0) {
			free(data);
		}
		else {
			free(data);
			*violation = "error converting file contents to UTF-8 for tag value";
			return false;
		}

		/* create and entry and append it */
		if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, field->field_name, converted)) {
			free(converted);
			*violation = "file for tag value is not valid UTF-8";
			return false;
		}
		free(converted);
		if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/false)) {
			*violation = "memory allocation failure";
			return false;
		}

		*needs_write = true;
		return true;
	}
	else {
		FLAC__bool needs_free = false;
		if(raw) {
			entry.entry = (FLAC__byte *)field->field;
		}
		else if(utf8_encode(field->field, &converted) >= 0) {
			entry.entry = (FLAC__byte *)converted;
			needs_free = true;
		}
		else {
			*violation = "error converting comment to UTF-8";
			return false;
		}
		entry.length = strlen((const char *)entry.entry);
		if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) {
			if(needs_free)
				free(converted);
			/*
			 * our previous parsing has already established that the field
			 * name is OK, so it must be the field value
			 */
			*violation = "tag value for is not valid UTF-8";
			return false;
		}

		if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) {
			if(needs_free)
				free(converted);
			*violation = "memory allocation failure";
			return false;
		}

		*needs_write = true;
		if(needs_free)
			free(converted);
		return true;
	}
}
Exemple #2
0
FLAC__StreamMetadata *grabbag__picture_parse_specification(const char *spec, const char **error_message)
{
	FLAC__StreamMetadata *obj;
	int state = 0;
	static const char *error_messages[] = {
		"memory allocation error",
		"invalid picture specification",
		"invalid picture specification: can't parse resolution/color part",
		"unable to extract resolution and color info from URL, user must set explicitly",
		"unable to extract resolution and color info from file, user must set explicitly",
		"error opening picture file",
		"error reading picture file",
		"invalid picture type",
		"unable to guess MIME type from file, user must set explicitly",
		"type 1 icon must be a 32x32 pixel PNG"
	};

	FLAC__ASSERT(0 != spec);
	FLAC__ASSERT(0 != error_message);

	/* double protection */
	if(0 == spec)
		return 0;
	if(0 == error_message)
		return 0;

	*error_message = 0;

	if(0 == (obj = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE))) {
		*error_message = error_messages[0];
		return obj;
	}

	if(strchr(spec, '|')) { /* full format */
		const char *p;
		char *q;
		for(p = spec; *error_message==0 && *p; ) {
			if(*p == '|') {
				switch(state) {
					case 0: /* type */
						if(!local__parse_type_(spec, p-spec, &obj->data.picture))
							*error_message = error_messages[7];
						break;
					case 1: /* mime type */
						if(p-spec) { /* if blank, we'll try to guess later from the picture data */
							if(0 == (q = local__strndup_(spec, p-spec)))
								*error_message = error_messages[0];
							else if(!FLAC__metadata_object_picture_set_mime_type(obj, q, /*copy=*/false))
								*error_message = error_messages[0];
						}
						break;
					case 2: /* description */
						if(0 == (q = local__strndup_(spec, p-spec)))
							*error_message = error_messages[0];
						else if(!FLAC__metadata_object_picture_set_description(obj, (FLAC__byte*)q, /*copy=*/false))
							*error_message = error_messages[0];
						break;
					case 3: /* resolution/color (e.g. [300x300x16[/1234]] */
						if(!local__parse_resolution_(spec, p-spec, &obj->data.picture))
							*error_message = error_messages[2];
						break;
					default:
						*error_message = error_messages[1];
						break;
				}
				p++;
				spec = p;
				state++;
			}
			else
				p++;
		}
	}
	else { /* simple format, filename only, everything else guessed */
		if(!local__parse_type_("", 0, &obj->data.picture)) /* use default picture type */
			*error_message = error_messages[7];
		/* leave MIME type to be filled in later */
		/* leave description empty */
		/* leave the rest to be filled in later: */
		else if(!local__parse_resolution_("", 0, &obj->data.picture))
			*error_message = error_messages[2];
		else
			state = 4;
	}

	/* parse filename, read file, try to extract resolution/color info if needed */
	if(*error_message == 0) {
		if(state != 4)
			*error_message = error_messages[1];
		else { /* 'spec' points to filename/URL */
			if(0 == strcmp(obj->data.picture.mime_type, "-->")) { /* magic MIME type means URL */
				if(!FLAC__metadata_object_picture_set_data(obj, (FLAC__byte*)spec, strlen(spec), /*copy=*/true))
					*error_message = error_messages[0];
				else if(obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0)
					*error_message = error_messages[3];
			}
			else { /* regular picture file */
				const FLAC__off_t size = grabbag__file_get_filesize(spec);
				if(size < 0)
					*error_message = error_messages[5];
				else {
					FLAC__byte *buffer = safe_malloc_(size);
					if(0 == buffer)
						*error_message = error_messages[0];
					else {
						FILE *f = flac_fopen(spec, "rb");
						if(0 == f) {
							*error_message = error_messages[5];
							free(buffer);
						}
						else {
							if(fread(buffer, 1, size, f) != (size_t)size)
								*error_message = error_messages[6];
							fclose(f);
							if(0 == *error_message) {
								if(!FLAC__metadata_object_picture_set_data(obj, buffer, size, /*copy=*/false))
									*error_message = error_messages[6];
								/* try to extract MIME type if user left it blank */
								else if(*obj->data.picture.mime_type == '\0' && !local__extract_mime_type_(obj))
									*error_message = error_messages[8];
								/* try to extract resolution/color info if user left it blank */
								else if((obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0) && !local__extract_resolution_color_info_(&obj->data.picture))
									*error_message = error_messages[4];
							}
							else {
								free(buffer);
							}
						}
					}
				}
			}
		}
	}

	if(*error_message == 0) {
		if(
			obj->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD &&
			(
				(strcmp(obj->data.picture.mime_type, "image/png") && strcmp(obj->data.picture.mime_type, "-->")) ||
				obj->data.picture.width != 32 ||
				obj->data.picture.height != 32
			)
		)
			*error_message = error_messages[9];
	}

	if(*error_message && obj) {
		FLAC__metadata_object_delete(obj);
		obj = 0;
	}

	return obj;
}