Merge branch 'runtime_enumeration' of git://github.com/eugene2k/MPD

This commit is contained in:
Max Kellermann 2019-07-01 09:48:33 +02:00
commit 44aaf51345
12 changed files with 137 additions and 32 deletions

View File

@ -21,15 +21,54 @@
#include "util/StringCompare.hxx" #include "util/StringCompare.hxx"
#include <assert.h> #include <assert.h>
#include <algorithm>
#include <iterator>
bool bool
InputPlugin::SupportsUri(const char *uri) const noexcept InputPlugin::SupportsUri(const char *uri) const noexcept
{ {
assert(prefixes != nullptr); assert(prefixes || protocols);
if (prefixes != nullptr) {
for (auto i = prefixes; *i != nullptr; ++i) for (auto i = prefixes; *i != nullptr; ++i)
if (StringStartsWithIgnoreCase(uri, *i)) if (StringStartsWithIgnoreCase(uri, *i))
return true; return true;
} else {
for (auto schema : protocols()) {
if (StringStartsWithIgnoreCase(uri, schema.c_str())){
return true;
}
}
}
return false; return false;
} }
// Note: The whitelist has to be ordered alphabetically
constexpr static const char *whitelist[] = {
"ftp",
"ftps",
"gopher",
"http",
"https",
"mmsh",
"mmst",
"rtmp",
"rtmpe",
"rtmps",
"rtmpt",
"rtmpte",
"rtmpts",
"rtp",
"scp",
"sftp",
"smb",
"srtp",
};
bool
protocol_is_whitelisted(const char *proto) {
auto begin = std::begin(whitelist);
auto end = std::end(whitelist);
return std::binary_search(begin, end, proto, [](const char* a, const char* b) {
return strcasecmp(a,b) < 0;
});
}

View File

@ -23,6 +23,9 @@
#include "Ptr.hxx" #include "Ptr.hxx"
#include "thread/Mutex.hxx" #include "thread/Mutex.hxx"
#include "util/Compiler.h" #include "util/Compiler.h"
#include <assert.h>
#include <set>
#include <string>
struct ConfigBlock; struct ConfigBlock;
class EventLoop; class EventLoop;
@ -62,6 +65,11 @@ struct InputPlugin {
*/ */
InputStreamPtr (*open)(const char *uri, Mutex &mutex); InputStreamPtr (*open)(const char *uri, Mutex &mutex);
/**
* return a set of supported protocols
*/
std::set<std::string> (*protocols)();
/** /**
* Prepare a #RemoteTagScanner. The operation must be started * Prepare a #RemoteTagScanner. The operation must be started
* using RemoteTagScanner::Start(). Returns nullptr if the * using RemoteTagScanner::Start(). Returns nullptr if the
@ -76,6 +84,25 @@ struct InputPlugin {
gcc_pure gcc_pure
bool SupportsUri(const char *uri) const noexcept; bool SupportsUri(const char *uri) const noexcept;
template<typename F>
void ForeachSupportedUri(F lambda) const noexcept {
assert(prefixes || protocols);
if (prefixes != nullptr) {
for (auto schema = prefixes; *schema != nullptr; ++schema) {
lambda(*schema);
}
}
if (protocols != nullptr) {
for (auto schema : protocols()) {
lambda(schema.c_str());
}
}
}
}; };
bool
protocol_is_whitelisted(const char *proto);
#endif #endif

View File

@ -486,4 +486,5 @@ const struct InputPlugin input_plugin_alsa = {
alsa_input_init, alsa_input_init,
nullptr, nullptr,
alsa_input_open, alsa_input_open,
nullptr
}; };

View File

@ -360,4 +360,5 @@ const InputPlugin input_plugin_cdio_paranoia = {
input_cdio_init, input_cdio_init,
nullptr, nullptr,
input_cdio_open, input_cdio_open,
nullptr
}; };

View File

@ -470,16 +470,25 @@ input_curl_open(const char *url, Mutex &mutex)
return CurlInputStream::Open(url, {}, mutex); return CurlInputStream::Open(url, {}, mutex);
} }
static constexpr const char *curl_prefixes[] = { static std::set<std::string>
"http://", input_curl_protocols() {
"https://", std::set<std::string> protocols;
nullptr auto version_info = curl_version_info(CURLVERSION_FIRST);
}; for (auto proto_ptr = version_info->protocols; *proto_ptr != nullptr; proto_ptr++) {
if (protocol_is_whitelisted(*proto_ptr)) {
std::string schema(*proto_ptr);
schema.append("://");
protocols.emplace(schema);
}
}
return protocols;
}
const struct InputPlugin input_plugin_curl = { const struct InputPlugin input_plugin_curl = {
"curl", "curl",
curl_prefixes, nullptr,
input_curl_init, input_curl_init,
input_curl_finish, input_curl_finish,
input_curl_open, input_curl_open,
input_curl_protocols
}; };

View File

@ -25,8 +25,13 @@
#include "lib/ffmpeg/Init.hxx" #include "lib/ffmpeg/Init.hxx"
#include "lib/ffmpeg/Error.hxx" #include "lib/ffmpeg/Error.hxx"
#include "../InputStream.hxx" #include "../InputStream.hxx"
#include "../InputPlugin.hxx"
#include "PluginUnavailable.hxx" #include "PluginUnavailable.hxx"
#include "util/StringAPI.hxx"
#include "../InputPlugin.hxx"
#include <iostream>
#include <exception>
#include <typeinfo>
#include <stdexcept>
class FfmpegInputStream final : public InputStream { class FfmpegInputStream final : public InputStream {
Ffmpeg::IOContext io; Ffmpeg::IOContext io;
@ -73,6 +78,22 @@ input_ffmpeg_init(EventLoop &, const ConfigBlock &)
throw PluginUnavailable("No protocol"); throw PluginUnavailable("No protocol");
} }
static std::set<std::string>
input_ffmpeg_protocols() {
void *opaque = nullptr;
const char* protocol;
std::set<std::string> protocols;
while ((protocol = avio_enum_protocols(&opaque, 0))) {
if (protocol_is_whitelisted(protocol)) {
std::string schema(protocol);
schema.append("://");
protocols.emplace(schema);
}
}
return protocols;
}
static InputStreamPtr static InputStreamPtr
input_ffmpeg_open(const char *uri, input_ffmpeg_open(const char *uri,
Mutex &mutex) Mutex &mutex)
@ -114,20 +135,11 @@ FfmpegInputStream::Seek(std::unique_lock<Mutex> &, offset_type new_offset)
offset = result; offset = result;
} }
static constexpr const char *ffmpeg_prefixes[] = {
"gopher://",
"rtp://",
"rtsp://",
"rtmp://",
"rtmpt://",
"rtmps://",
nullptr
};
const InputPlugin input_plugin_ffmpeg = { const InputPlugin input_plugin_ffmpeg = {
"ffmpeg", "ffmpeg",
ffmpeg_prefixes, nullptr,
input_ffmpeg_init, input_ffmpeg_init,
nullptr, nullptr,
input_ffmpeg_open, input_ffmpeg_open,
input_ffmpeg_protocols
}; };

View File

@ -110,4 +110,5 @@ const InputPlugin input_plugin_mms = {
nullptr, nullptr,
nullptr, nullptr,
input_mms_open, input_mms_open,
nullptr
}; };

View File

@ -232,4 +232,5 @@ const InputPlugin input_plugin_nfs = {
input_nfs_init, input_nfs_init,
input_nfs_finish, input_nfs_finish,
input_nfs_open, input_nfs_open,
nullptr
}; };

View File

@ -219,5 +219,6 @@ const InputPlugin qobuz_input_plugin = {
InitQobuzInput, InitQobuzInput,
FinishQobuzInput, FinishQobuzInput,
OpenQobuzInput, OpenQobuzInput,
nullptr,
ScanQobuzTags, ScanQobuzTags,
}; };

View File

@ -166,4 +166,5 @@ const InputPlugin input_plugin_smbclient = {
input_smbclient_init, input_smbclient_init,
nullptr, nullptr,
input_smbclient_open, input_smbclient_open,
nullptr
}; };

View File

@ -251,5 +251,6 @@ const InputPlugin tidal_input_plugin = {
InitTidalInput, InitTidalInput,
FinishTidalInput, FinishTidalInput,
OpenTidalInput, OpenTidalInput,
nullptr,
ScanTidalTags, ScanTidalTags,
}; };

View File

@ -22,28 +22,41 @@
#include "input/Registry.hxx" #include "input/Registry.hxx"
#include "input/InputPlugin.hxx" #include "input/InputPlugin.hxx"
#include "client/Response.hxx" #include "client/Response.hxx"
#include "util/ASCII.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
#include <assert.h> #include <assert.h>
#include <string>
void print_supported_uri_schemes_to_fp(FILE *fp) void print_supported_uri_schemes_to_fp(FILE *fp)
{ {
#ifdef HAVE_UN #ifdef HAVE_UN
fprintf(fp, " file://"); fprintf(fp, " file://");
#endif #endif
std::set<std::string> protocols;
input_plugins_for_each(plugin) input_plugins_for_each(plugin)
for (auto i = plugin->prefixes; *i != nullptr; ++i) plugin->ForeachSupportedUri([&](const char* uri) {
fprintf(fp, " %s", *i); protocols.emplace(uri);
});
for (auto protocol : protocols) {
fprintf(fp, " %s", protocol.c_str());
}
fprintf(fp,"\n"); fprintf(fp,"\n");
} }
void void
print_supported_uri_schemes(Response &r) print_supported_uri_schemes(Response &r)
{ {
std::set<std::string> protocols;
input_plugins_for_each_enabled(plugin) input_plugins_for_each_enabled(plugin)
for (auto i = plugin->prefixes; *i != nullptr; ++i) plugin->ForeachSupportedUri([&](const char* uri) {
r.Format("handler: %s\n", *i); protocols.emplace(uri);
});
for (auto protocol : protocols) {
r.Format("handler: %s\n", protocol.c_str());
}
} }
bool bool
@ -52,9 +65,7 @@ uri_supported_scheme(const char *uri) noexcept
assert(uri_has_scheme(uri)); assert(uri_has_scheme(uri));
input_plugins_for_each_enabled(plugin) input_plugins_for_each_enabled(plugin)
for (auto i = plugin->prefixes; *i != nullptr; ++i) return plugin->SupportsUri(uri);
if (StringStartsWithCaseASCII(uri, *i))
return true;
return false; return false;
} }