diff --git a/repertory/librepertory/include/platform/unix_platform.hpp b/repertory/librepertory/include/platform/unix_platform.hpp index e4b9daa2..fbb9ceab 100644 --- a/repertory/librepertory/include/platform/unix_platform.hpp +++ b/repertory/librepertory/include/platform/unix_platform.hpp @@ -28,17 +28,6 @@ namespace repertory { class i_provider; -struct create_autostart_opts final { - std::string app_name; - std::optional comment; - bool enabled{true}; - std::vector exec_args; - std::string exec_path; - std::optional icon_path; - std::vector only_show_in; - bool terminal{false}; -}; - class lock_data final { public: lock_data(provider_type prov, std::string_view unique_id); @@ -82,8 +71,6 @@ public: -> bool; }; -[[nodiscard]] auto create_autostart_entry(create_autostart_opts opts) -> bool; - [[nodiscard]] auto create_meta_attributes( std::uint64_t accessed_date, std::uint32_t attributes, std::uint64_t changed_date, std::uint64_t creation_date, bool directory, @@ -94,8 +81,6 @@ public: [[nodiscard]] auto provider_meta_handler(i_provider &provider, bool directory, const api_file &file) -> api_error; - -[[nodiscard]] auto remove_autostart_entry(std::string_view name) -> bool; } // namespace repertory #endif // !defined(_WIN32) diff --git a/repertory/librepertory/src/platform/unix_platform.cpp b/repertory/librepertory/src/platform/unix_platform.cpp index bdae1f42..e87487fc 100644 --- a/repertory/librepertory/src/platform/unix_platform.cpp +++ b/repertory/librepertory/src/platform/unix_platform.cpp @@ -19,7 +19,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include #if !defined(_WIN32) #include "platform/platform.hpp" @@ -29,85 +28,13 @@ #include "providers/i_provider.hpp" #include "types/startup_exception.hpp" #include "utils/common.hpp" +#include "utils/config.hpp" #include "utils/error_utils.hpp" #include "utils/file_utils.hpp" #include "utils/path.hpp" #include "utils/string.hpp" #include "utils/unix.hpp" -namespace { -[[nodiscard]] auto get_autostart_dir() -> std::string { - auto config = repertory::utils::get_environment_variable("XDG_CONFIG_HOME"); - if (config.empty()) { - config = repertory::utils::path::combine( - repertory::utils::get_environment_variable("HOME"), {".config"}); - } - - return repertory::utils::path::combine(config, {"autostart"}); -} - -[[nodiscard]] auto join_args_for_exec(const std::vector &args) - -> std::string { - std::string str; - for (const auto &arg : args) { - if (not str.empty()) { - str += ' '; - } - - auto needs_quotes = arg.find_first_of(" \t\"'\\$`") != std::string::npos; - if (needs_quotes) { - str += '"'; - for (const auto &cur_ch : arg) { - if (cur_ch == '"' || cur_ch == '\\') { - str += '\\'; - } - str += cur_ch; - } - str += '"'; - } else { - str += arg; - } - } - - return str; -} - -[[nodiscard]] auto sanitize_basename(std::string_view app_name) -> std::string { - std::string out; - out.reserve(app_name.size()); - for (const auto &cur_ch : app_name) { - if ((cur_ch >= 'a' && cur_ch <= 'z') || (cur_ch >= '0' && cur_ch <= '9') || - (cur_ch == '-' || cur_ch == '_')) { - out.push_back(cur_ch); - } else if (cur_ch >= 'A' && cur_ch <= 'Z') { - out.push_back(static_cast(cur_ch - 'A' + 'a')); - } else { - out.push_back('-'); // replace spaces/symbols - } - } - - std::string collapsed; - collapsed.reserve(out.size()); - bool prev_dash = false; - for (const auto &cur_ch : out) { - if (cur_ch == '-') { - if (not prev_dash) { - collapsed.push_back(cur_ch); - } - prev_dash = true; - } else { - collapsed.push_back(cur_ch); - prev_dash = false; - } - } - - if (collapsed.empty()) { - collapsed = "app"; - } - return collapsed; -} -} // namespace - namespace repertory { lock_data::lock_data(provider_type prov, std::string_view unique_id) : mutex_id_(create_lock_id(prov, unique_id)) { @@ -328,102 +255,6 @@ auto provider_meta_handler(i_provider &provider, bool directory, return res; } - -[[nodiscard]] static auto desktop_file_path_for(std::string_view app_name) - -> std::string { - return utils::path::combine(get_autostart_dir(), - {sanitize_basename(app_name) + ".desktop"}); -} - -auto create_autostart_entry(create_autostart_opts opts) -> bool { - REPERTORY_USES_FUNCTION_NAME(); - - auto file = desktop_file_path_for(opts.app_name); - if (utils::file::file{file}.exists()) { - return true; - } - - auto dir = get_autostart_dir(); - if (dir.empty()) { - return false; - } - - if (not utils::file::directory{dir}.create_directory()) { - return false; - } - - auto exec_line = opts.exec_path; - if (not opts.exec_args.empty()) { - exec_line += ' '; - exec_line += join_args_for_exec(opts.exec_args); - } - - std::ofstream out(file, std::ios::binary | std::ios::trunc); - if (not out) { - return false; - } - - out << "[Desktop Entry]\n"; - out << "Type=Application\n"; - out << "Version=1.0\n"; - out << "Name=" << opts.app_name << "\n"; - out << "Exec=" << exec_line << "\n"; - out << "Terminal=" << (opts.terminal ? "true" : "false") << "\n"; - - if (opts.comment && !opts.comment->empty()) { - out << "Comment=" << *opts.comment << "\n"; - } - - if (opts.icon_path && !opts.icon_path->empty()) { - out << "Icon=" << *opts.icon_path << "\n"; - } - - if (!opts.only_show_in.empty()) { - out << "OnlyShowIn="; - for (std::size_t idx = 0U; idx < opts.only_show_in.size(); ++idx) { - if (idx != 0U) { - out << ';'; - } - out << opts.only_show_in[idx]; - } - out << ";\n"; - } - - if (not opts.enabled) { - out << "X-GNOME-Autostart-enabled=false\n"; - out << "Hidden=true\n"; - } - - out.flush(); - if (not out) { - return false; - } - -#if defined(__linux__) || defined(__APPLE__) - chmod(file.c_str(), 0644); -#endif // defined(__linux__) || defined(__APPLE__) - - utils::error::handle_info( - function_name, fmt::format("created auto-start entry|path|{}", file)); - return true; -} - -auto remove_autostart_entry(std::string_view name) -> bool { - REPERTORY_USES_FUNCTION_NAME(); - - auto file = desktop_file_path_for(name); - if (not utils::file::file{file}.exists()) { - return true; - } - - auto ret = utils::file::file{file}.remove(); - if (ret) { - utils::error::handle_info( - function_name, fmt::format("removed auto-start entry|path|{}", file)); - } - - return ret; -} } // namespace repertory #endif //_WIN32 diff --git a/repertory/repertory/src/ui/mgmt_app_config.cpp b/repertory/repertory/src/ui/mgmt_app_config.cpp index 0e7955fa..7405f888 100644 --- a/repertory/repertory/src/ui/mgmt_app_config.cpp +++ b/repertory/repertory/src/ui/mgmt_app_config.cpp @@ -217,7 +217,7 @@ void mgmt_app_config::set_auto_start(bool auto_start) { if (utils::file::change_to_process_directory()) { #if defined(__linux__) if (auto_start) { - create_autostart_opts opts{}; + utils::create_autostart_opts opts{}; opts.app_name = "repertory"; opts.comment = "Mount utility for AWS S3 and Sia"; opts.exec_args = {"-ui", "-lo"}; @@ -225,17 +225,33 @@ void mgmt_app_config::set_auto_start(bool auto_start) { opts.icon_path = utils::path::combine(".", {"repertory.png"}); opts.terminal = true; - if (not create_autostart_entry(opts)) { + if (not utils::create_autostart_entry(opts)) { utils::error::raise_error( function_name, utils::get_last_error_code(), fmt::format("failed to create auto-start entry")); } - } else if (not remove_autostart_entry("repertory")) { + } else if (not utils::remove_autostart_entry("repertory")) { utils::error::raise_error( function_name, utils::get_last_error_code(), fmt::format("failed to remove auto-start entry")); } #endif // defined(__linux__) + +#if defined(_WIN32) + if (auto_start) { + if (not utils::create_shortcut( + utils::path::combine(L".", {L"repertory"}), {L"-ui", L"-lo"}, + utils::path::absolute(L"."), L"repertory")) { + utils::error::raise_error( + function_name, utils::get_last_error_code(), + fmt::format("failed to create auto-start entry")); + } + } else if (not utils::remove_shortcut(L"repertory")) { + utils::error::raise_error( + function_name, utils::get_last_error_code(), + fmt::format("failed to remove auto-start entry")); + } +#endif // defined(_WIN32) } else { utils::error::raise_error(function_name, utils::get_last_error_code(), fmt::format("failed to change directory")); diff --git a/support/include/utils/unix.hpp b/support/include/utils/unix.hpp index de0a7880..273eff6f 100644 --- a/support/include/utils/unix.hpp +++ b/support/include/utils/unix.hpp @@ -27,6 +27,19 @@ #include "utils/config.hpp" namespace repertory::utils { +#if defined(__linux__) +struct create_autostart_opts final { + std::string app_name; + std::optional comment; + bool enabled{true}; + std::vector exec_args; + std::string exec_path; + std::optional icon_path; + std::vector only_show_in; + bool terminal{false}; +}; +#endif // defined(__linux__) + using passwd_callback_t = std::function; #if defined(__APPLE__) @@ -37,6 +50,10 @@ template [[nodiscard]] auto convert_to_uint64(const pthread_t &thread) -> std::uint64_t; #endif // defined(__APPLE__) +#if defined(__linux__) +[[nodiscard]] auto create_autostart_entry(create_autostart_opts opts) -> bool; +#endif // defined(__linux__) + [[nodiscard]] auto get_last_error_code() -> int; [[nodiscard]] auto get_thread_id() -> std::uint64_t; @@ -48,6 +65,10 @@ void set_last_error_code(int error_code); [[nodiscard]] auto use_getpwuid(uid_t uid, passwd_callback_t callback) -> utils::result; +#if defined(__linux__) +[[nodiscard]] auto remove_autostart_entry(std::string_view name) -> bool; +#endif // defined(__linux__) + // template implementations #if defined(__APPLE__) template diff --git a/support/include/utils/windows.hpp b/support/include/utils/windows.hpp index 35017b7f..1b97db22 100644 --- a/support/include/utils/windows.hpp +++ b/support/include/utils/windows.hpp @@ -50,6 +50,10 @@ auto create_shortcut(const std::wstring &exe_path, const std::wstring &location = get_startup_folder()) -> bool; +[[nodiscard]] auto +remove_shortcut(std::wstring shortcut_name, + const std::wstring &location = get_startup_folder()) -> bool; + void set_last_error_code(DWORD error_code); } // namespace repertory::utils diff --git a/support/src/utils/unix.cpp b/support/src/utils/unix.cpp index 694e34db..f004dd03 100644 --- a/support/src/utils/unix.cpp +++ b/support/src/utils/unix.cpp @@ -24,7 +24,10 @@ #include "utils/unix.hpp" #include "utils/collection.hpp" +#include "utils/config.hpp" #include "utils/error.hpp" +#include "utils/file.hpp" +#include "utils/path.hpp" namespace { [[nodiscard]] auto get_group_list(auto *pass) -> std::vector { @@ -86,6 +89,87 @@ namespace { return groups; } + +#if defined(__linux__) +[[nodiscard]] auto sanitize_basename(std::string_view app_name) -> std::string { + std::string out; + out.reserve(app_name.size()); + for (const auto &cur_ch : app_name) { + if ((cur_ch >= 'a' && cur_ch <= 'z') || (cur_ch >= '0' && cur_ch <= '9') || + (cur_ch == '-' || cur_ch == '_')) { + out.push_back(cur_ch); + } else if (cur_ch >= 'A' && cur_ch <= 'Z') { + out.push_back(static_cast(cur_ch - 'A' + 'a')); + } else { + out.push_back('-'); // replace spaces/symbols + } + } + + std::string collapsed; + collapsed.reserve(out.size()); + bool prev_dash = false; + for (const auto &cur_ch : out) { + if (cur_ch == '-') { + if (not prev_dash) { + collapsed.push_back(cur_ch); + } + prev_dash = true; + } else { + collapsed.push_back(cur_ch); + prev_dash = false; + } + } + + if (collapsed.empty()) { + collapsed = "app"; + } + return collapsed; +} + +[[nodiscard]] auto get_autostart_dir() -> std::string { + auto config = repertory::utils::get_environment_variable("XDG_CONFIG_HOME"); + if (config.empty()) { + config = repertory::utils::path::combine( + repertory::utils::get_environment_variable("HOME"), {".config"}); + } + + return repertory::utils::path::combine(config, {"autostart"}); +} + +[[nodiscard]] auto desktop_file_path_for(std::string_view app_name) + -> std::string { + return repertory::utils::path::combine( + get_autostart_dir(), { + sanitize_basename(app_name) + ".desktop", + }); +} + +[[nodiscard]] auto join_args_for_exec(const std::vector &args) + -> std::string { + std::string str; + for (const auto &arg : args) { + if (not str.empty()) { + str += ' '; + } + + auto needs_quotes = arg.find_first_of(" \t\"'\\$`") != std::string::npos; + if (needs_quotes) { + str += '"'; + for (const auto &cur_ch : arg) { + if (cur_ch == '"' || cur_ch == '\\') { + str += '\\'; + } + str += cur_ch; + } + str += '"'; + } else { + str += arg; + } + } + + return str; +} +#endif // defined(__linux__) } // namespace namespace repertory::utils { @@ -95,6 +179,81 @@ auto convert_to_uint64(const pthread_t &thread) -> std::uint64_t { } #endif // !defined(__APPLE__) +#if defined(__linux__) +auto create_autostart_entry(create_autostart_opts opts) -> bool { + REPERTORY_USES_FUNCTION_NAME(); + + auto file = desktop_file_path_for(opts.app_name); + if (utils::file::file{file}.exists()) { + return true; + } + + auto dir = get_autostart_dir(); + if (dir.empty()) { + return false; + } + + if (not utils::file::directory{dir}.create_directory()) { + return false; + } + + auto exec_line = opts.exec_path; + if (not opts.exec_args.empty()) { + exec_line += ' '; + exec_line += join_args_for_exec(opts.exec_args); + } + + std::ofstream out(file, std::ios::binary | std::ios::trunc); + if (not out) { + return false; + } + + out << "[Desktop Entry]\n"; + out << "Type=Application\n"; + out << "Version=1.0\n"; + out << "Name=" << opts.app_name << "\n"; + out << "Exec=" << exec_line << "\n"; + out << "Terminal=" << (opts.terminal ? "true" : "false") << "\n"; + + if (opts.comment && !opts.comment->empty()) { + out << "Comment=" << *opts.comment << "\n"; + } + + if (opts.icon_path && !opts.icon_path->empty()) { + out << "Icon=" << *opts.icon_path << "\n"; + } + + if (!opts.only_show_in.empty()) { + out << "OnlyShowIn="; + for (std::size_t idx = 0U; idx < opts.only_show_in.size(); ++idx) { + if (idx != 0U) { + out << ';'; + } + out << opts.only_show_in[idx]; + } + out << ";\n"; + } + + if (not opts.enabled) { + out << "X-GNOME-Autostart-enabled=false\n"; + out << "Hidden=true\n"; + } + + out.flush(); + if (not out) { + return false; + } + +#if defined(__linux__) || defined(__APPLE__) + chmod(file.c_str(), 0644); +#endif // defined(__linux__) || defined(__APPLE__) + + utils::error::handle_info( + function_name, fmt::format("created auto-start entry|path|{}", file)); + return true; +} +#endif // defined(__linux__) + auto get_last_error_code() -> int { return errno; } auto get_thread_id() -> std::uint64_t { @@ -115,6 +274,25 @@ auto is_uid_member_of_group(uid_t uid, gid_t gid) -> bool { return collection::includes(groups, gid); } +#if defined(__linux__) +auto remove_autostart_entry(std::string_view name) -> bool { + REPERTORY_USES_FUNCTION_NAME(); + + auto file = desktop_file_path_for(name); + if (not utils::file::file{file}.exists()) { + return true; + } + + auto ret = utils::file::file{file}.remove(); + if (ret) { + utils::error::handle_info( + function_name, fmt::format("removed auto-start entry|path|{}", file)); + } + + return ret; +} +#endif // defined(__linux__) + auto use_getpwuid(uid_t uid, passwd_callback_t callback) -> result { REPERTORY_USES_FUNCTION_NAME(); diff --git a/support/src/utils/windows.cpp b/support/src/utils/windows.cpp index 46a42ea5..b81300db 100644 --- a/support/src/utils/windows.cpp +++ b/support/src/utils/windows.cpp @@ -24,6 +24,8 @@ #include "utils/windows.hpp" #include "utils/error.hpp" +#include "utils/file.hpp" +#include "utils/path.hpp" #include "utils/string.hpp" namespace repertory::utils { @@ -273,6 +275,19 @@ auto create_shortcut(const std::wstring &exe_path, return true; } +auto remove_shortcut(std::wstring shortcut_name, const std::wstring &location) + -> bool { + if (not shortcut_name.ends_with(L".lnk")) { + shortcut_name += L".lnk"; + } + + auto file = utils::path::combine(location, {shortcut_name}); + if (not utils::file::file{file}.exists()) { + return true; + } + + return utils::file::file{file}.remove(); +} } // namespace repertory::utils #endif // defined(_WIN32)