demux_playlist: detect headerless m3u files by extension

m3u files are normally just text files with a list of filenames. Nothing
actually mandates that there is a header. Until now, we've rejected such
files, because there's absolutely no way to detect them.

If nothing else claims the file, the extension is ".m3u", and if the
contents of the file look like text, then load it as m3u playlist. The
text heuristic is pretty cheap, but at least it should prevent trying
to load binary data as playlist. (Which would "work", but result in a
catastrophic user experience.)

Due to the text heuristic, UTF-16/32 files will be rejected (unless they
have a header), but I don't care.
This commit is contained in:
wm4 2014-12-05 23:50:56 +01:00
parent f911ef08ab
commit 09c701b797
1 changed files with 24 additions and 1 deletions

View File

@ -47,6 +47,8 @@ struct pl_parser {
bool error; bool error;
bool probing; bool probing;
bool force; bool force;
enum demux_check check_level;
struct stream *real_stream;
}; };
static char *pl_get_line0(struct pl_parser *p) static char *pl_get_line0(struct pl_parser *p)
@ -79,11 +81,30 @@ static bool pl_eof(struct pl_parser *p)
return p->error || p->s->eof; return p->error || p->s->eof;
} }
static bool maybe_text(bstr d)
{
for (int n = 0; n < d.len; n++) {
unsigned char c = d.start[n];
if (c < 32 && c != '\n' && c != '\r' && c != '\t')
return false;
}
return true;
}
static int parse_m3u(struct pl_parser *p) static int parse_m3u(struct pl_parser *p)
{ {
bstr line = bstr_strip(pl_get_line(p)); bstr line = bstr_strip(pl_get_line(p));
if (p->probing && !bstr_equals0(line, "#EXTM3U")) if (p->probing && !bstr_equals0(line, "#EXTM3U")) {
// Last resort: if the file extension is m3u, it might be headerless.
if (p->check_level == DEMUX_CHECK_UNSAFE) {
char *ext = mp_splitext(p->real_stream->url, NULL);
bstr data = stream_peek(p->real_stream, PROBE_SIZE);
if (ext && strcmp(ext, "m3u") && data.len > 10 && maybe_text(data))
goto ok;
}
return -1; return -1;
}
ok:
if (p->probing) if (p->probing)
return 0; return 0;
while (line.len || !pl_eof(p)) { while (line.len || !pl_eof(p)) {
@ -223,12 +244,14 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
struct pl_parser *p = talloc_zero(NULL, struct pl_parser); struct pl_parser *p = talloc_zero(NULL, struct pl_parser);
p->log = demuxer->log; p->log = demuxer->log;
p->pl = talloc_zero(p, struct playlist); p->pl = talloc_zero(p, struct playlist);
p->real_stream = demuxer->stream;
bstr probe_buf = stream_peek(demuxer->stream, PROBE_SIZE); bstr probe_buf = stream_peek(demuxer->stream, PROBE_SIZE);
p->s = open_memory_stream(probe_buf.start, probe_buf.len); p->s = open_memory_stream(probe_buf.start, probe_buf.len);
p->s->mime_type = demuxer->stream->mime_type; p->s->mime_type = demuxer->stream->mime_type;
p->utf16 = stream_skip_bom(p->s); p->utf16 = stream_skip_bom(p->s);
p->force = force; p->force = force;
p->check_level = check;
p->probing = true; p->probing = true;
const struct pl_format *fmt = probe_pl(p); const struct pl_format *fmt = probe_pl(p);
free_stream(p->s); free_stream(p->s);