From 48766449e5f0385476d21299eb9190bbc4bd2a15 Mon Sep 17 00:00:00 2001 From: demostanis Date: Fri, 22 Jul 2022 18:03:22 +0200 Subject: [PATCH] LaunchServer+LibDesktop: Open from mime type Before, LaunchServer would only open files based on their extension. This wouldn't work if the file had the wrong one. --- Userland/Libraries/LibDesktop/AppFile.cpp | 11 ++++ Userland/Libraries/LibDesktop/AppFile.h | 1 + Userland/Services/LaunchServer/Launcher.cpp | 62 +++++++++++++++++---- Userland/Services/LaunchServer/Launcher.h | 3 + 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/Userland/Libraries/LibDesktop/AppFile.cpp b/Userland/Libraries/LibDesktop/AppFile.cpp index 1b7e0c9920..1ef20f3986 100644 --- a/Userland/Libraries/LibDesktop/AppFile.cpp +++ b/Userland/Libraries/LibDesktop/AppFile.cpp @@ -102,6 +102,17 @@ bool AppFile::run_in_terminal() const return m_config->read_bool_entry("App", "RunInTerminal", false); } +Vector AppFile::launcher_mime_types() const +{ + Vector mime_types; + for (auto& entry : m_config->read_entry("Launcher", "MimeTypes").split(',')) { + entry = entry.trim_whitespace(); + if (!entry.is_empty()) + mime_types.append(entry); + } + return mime_types; +} + Vector AppFile::launcher_file_types() const { Vector file_types; diff --git a/Userland/Libraries/LibDesktop/AppFile.h b/Userland/Libraries/LibDesktop/AppFile.h index cb393db6e2..0930fb8dac 100644 --- a/Userland/Libraries/LibDesktop/AppFile.h +++ b/Userland/Libraries/LibDesktop/AppFile.h @@ -32,6 +32,7 @@ public: String icon_path() const; GUI::Icon icon() const; bool run_in_terminal() const; + Vector launcher_mime_types() const; Vector launcher_file_types() const; Vector launcher_protocols() const; bool spawn() const; diff --git a/Userland/Services/LaunchServer/Launcher.cpp b/Userland/Services/LaunchServer/Launcher.cpp index 63fa8ac5e6..0de533fc40 100644 --- a/Userland/Services/LaunchServer/Launcher.cpp +++ b/Userland/Services/LaunchServer/Launcher.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,9 @@ void Launcher::load_handlers(String const& af_dir) Desktop::AppFile::for_each([&](auto af) { auto app_name = af->name(); auto app_executable = af->executable(); + HashTable mime_types; + for (auto& mime_type : af->launcher_mime_types()) + mime_types.set(mime_type); HashTable file_types; for (auto& file_type : af->launcher_file_types()) file_types.set(file_type); @@ -90,13 +94,22 @@ void Launcher::load_handlers(String const& af_dir) for (auto& protocol : af->launcher_protocols()) protocols.set(protocol); if (access(app_executable.characters(), X_OK) == 0) - m_handlers.set(app_executable, { Handler::Type::Default, app_name, app_executable, file_types, protocols }); + m_handlers.set(app_executable, { Handler::Type::Default, app_name, app_executable, mime_types, file_types, protocols }); }, af_dir); } void Launcher::load_config(Core::ConfigFile const& cfg) { + for (auto key : cfg.keys("MimeType")) { + auto handler = cfg.read_entry("MimeType", key).trim_whitespace(); + if (handler.is_empty()) + continue; + if (access(handler.characters(), X_OK) != 0) + continue; + m_mime_handlers.set(key.to_lowercase(), handler); + } + for (auto key : cfg.keys("FileType")) { auto handler = cfg.read_entry("FileType", key).trim_whitespace(); if (handler.is_empty()) @@ -156,6 +169,20 @@ Vector Launcher::handlers_with_details_for_url(const URL& url) return handlers; } +Optional Launcher::mime_type_for_file(String path) +{ + auto file_or_error = Core::File::open(path, Core::OpenMode::ReadOnly); + if (file_or_error.is_error()) { + return {}; + } else { + auto file = file_or_error.release_value(); + // Read accounts for longest possible offset + signature we currently match against. + auto bytes = file->read(0x9006); + + return Core::guess_mime_type_based_on_sniffed_bytes(bytes.bytes()); + } +} + bool Launcher::open_url(const URL& url, String const& handler_name) { if (!handler_name.is_null()) @@ -275,12 +302,20 @@ void Launcher::for_each_handler_for_path(String const& path, Function bool { - if (handler.handler_type != Handler::Type::Default || handler.file_types.contains(extension)) - return f(handler); - return false; - }); + auto mime_type = mime_type_for_file(path); + if (mime_type.has_value()) { + for_each_handler(mime_type.value(), m_mime_handlers, [&](auto const& handler) -> bool { + if (handler.handler_type != Handler::Type::Default || handler.mime_types.contains(mime_type.value())) + return f(handler); + return false; + }); + } else { + for_each_handler(extension, m_file_handlers, [&](auto const& handler) -> bool { + if (handler.handler_type != Handler::Type::Default || handler.file_types.contains(extension)) + return f(handler); + return false; + }); + } } bool Launcher::open_file_url(const URL& url) @@ -312,10 +347,13 @@ bool Launcher::open_file_url(const URL& url) if ((st.st_mode & S_IFMT) == S_IFREG && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) return spawn(url.path(), {}); - auto extension_parts = url.path().to_lowercase().split('.'); - String extension = {}; - if (extension_parts.size() > 1) - extension = extension_parts.last(); + auto extension = LexicalPath::extension(url.path()).to_lowercase(); + auto mime_type = mime_type_for_file(url.path()); + + auto mime_type_or_extension = extension; + bool should_use_mime_type = mime_type.has_value() && m_mime_handlers.get(mime_type.value()).has_value(); + if (should_use_mime_type) + mime_type_or_extension = mime_type.value(); auto handler_optional = m_file_handlers.get("txt"); if (!handler_optional.has_value()) @@ -339,6 +377,6 @@ bool Launcher::open_file_url(const URL& url) additional_parameters.append(filepath); - return open_with_user_preferences(m_file_handlers, extension, additional_parameters, default_handler); + return open_with_user_preferences(should_use_mime_type ? m_mime_handlers : m_file_handlers, mime_type_or_extension, additional_parameters, default_handler); } } diff --git a/Userland/Services/LaunchServer/Launcher.h b/Userland/Services/LaunchServer/Launcher.h index e9f918775e..8662e43ac0 100644 --- a/Userland/Services/LaunchServer/Launcher.h +++ b/Userland/Services/LaunchServer/Launcher.h @@ -24,6 +24,7 @@ struct Handler { Type handler_type; String name; String executable; + HashTable mime_types {}; HashTable file_types {}; HashTable protocols {}; @@ -47,7 +48,9 @@ private: HashMap m_handlers; HashMap m_protocol_handlers; HashMap m_file_handlers; + HashMap m_mime_handlers; + Optional mime_type_for_file(String path); Handler get_handler_for_executable(Handler::Type, String const&) const; void for_each_handler(String const& key, HashMap& user_preferences, Function f); void for_each_handler_for_path(String const&, Function f);