AppFile.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. * Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
  3. * Copyright (c) 2021, Spencer Dixon <spencercdixon@gmail.com>
  4. * Copyright (c) 2022, the SerenityOS developers.
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Function.h>
  9. #include <AK/Vector.h>
  10. #include <LibCore/ConfigFile.h>
  11. #include <LibCore/DirIterator.h>
  12. #include <LibCore/Process.h>
  13. #include <LibCore/StandardPaths.h>
  14. #include <LibDesktop/AppFile.h>
  15. #include <LibFileSystem/FileSystem.h>
  16. #include <LibGUI/MessageBox.h>
  17. namespace Desktop {
  18. ByteString AppFile::app_file_path_for_app(StringView app_name)
  19. {
  20. return ByteString::formatted("{}/{}.af", APP_FILES_DIRECTORY, app_name);
  21. }
  22. bool AppFile::exists_for_app(StringView app_name)
  23. {
  24. return FileSystem::exists(app_file_path_for_app(app_name));
  25. }
  26. NonnullRefPtr<AppFile> AppFile::get_for_app(StringView app_name)
  27. {
  28. return open(app_file_path_for_app(app_name));
  29. }
  30. NonnullRefPtr<AppFile> AppFile::open(StringView path)
  31. {
  32. return adopt_ref(*new AppFile(path));
  33. }
  34. void AppFile::for_each(Function<void(NonnullRefPtr<AppFile>)> callback, StringView directory)
  35. {
  36. Core::DirIterator di(directory, Core::DirIterator::SkipDots);
  37. if (di.has_error())
  38. return;
  39. while (di.has_next()) {
  40. auto name = di.next_path();
  41. if (!name.ends_with(".af"sv))
  42. continue;
  43. auto path = ByteString::formatted("{}/{}", directory, name);
  44. auto af = AppFile::open(path);
  45. if (!af->is_valid())
  46. continue;
  47. callback(af);
  48. }
  49. }
  50. AppFile::AppFile(StringView path)
  51. : m_config(Core::ConfigFile::open(path).release_value_but_fixme_should_propagate_errors())
  52. , m_valid(validate())
  53. {
  54. }
  55. bool AppFile::validate() const
  56. {
  57. if (m_config->read_entry("App", "Name").trim_whitespace().is_empty())
  58. return false;
  59. if (m_config->read_entry("App", "Executable").trim_whitespace().is_empty())
  60. return false;
  61. return true;
  62. }
  63. ByteString AppFile::name() const
  64. {
  65. auto name = m_config->read_entry("App", "Name").trim_whitespace().replace("&"sv, ""sv);
  66. VERIFY(!name.is_empty());
  67. return name;
  68. }
  69. ByteString AppFile::menu_name() const
  70. {
  71. auto name = m_config->read_entry("App", "Name").trim_whitespace();
  72. VERIFY(!name.is_empty());
  73. return name;
  74. }
  75. ByteString AppFile::executable() const
  76. {
  77. auto executable = m_config->read_entry("App", "Executable").trim_whitespace();
  78. VERIFY(!executable.is_empty());
  79. return executable;
  80. }
  81. ByteString AppFile::description() const
  82. {
  83. return m_config->read_entry("App", "Description").trim_whitespace();
  84. }
  85. ByteString AppFile::category() const
  86. {
  87. return m_config->read_entry("App", "Category").trim_whitespace();
  88. }
  89. ByteString AppFile::working_directory() const
  90. {
  91. return m_config->read_entry("App", "WorkingDirectory").trim_whitespace();
  92. }
  93. ByteString AppFile::icon_path() const
  94. {
  95. return m_config->read_entry("App", "IconPath").trim_whitespace();
  96. }
  97. GUI::Icon AppFile::icon() const
  98. {
  99. auto override_icon = icon_path();
  100. // FIXME: support pointing to actual .ico files
  101. if (!override_icon.is_empty())
  102. return GUI::FileIconProvider::icon_for_path(override_icon);
  103. return GUI::FileIconProvider::icon_for_path(executable());
  104. }
  105. bool AppFile::run_in_terminal() const
  106. {
  107. return m_config->read_bool_entry("App", "RunInTerminal", false);
  108. }
  109. bool AppFile::requires_root() const
  110. {
  111. return m_config->read_bool_entry("App", "RequiresRoot", false);
  112. }
  113. bool AppFile::exclude_from_system_menu() const
  114. {
  115. return m_config->read_bool_entry("App", "ExcludeFromSystemMenu", false);
  116. }
  117. Vector<ByteString> AppFile::launcher_mime_types() const
  118. {
  119. Vector<ByteString> mime_types;
  120. for (auto& entry : m_config->read_entry("Launcher", "MimeTypes").split(',')) {
  121. entry = entry.trim_whitespace();
  122. if (!entry.is_empty())
  123. mime_types.append(entry);
  124. }
  125. return mime_types;
  126. }
  127. Vector<ByteString> AppFile::launcher_file_types() const
  128. {
  129. Vector<ByteString> file_types;
  130. for (auto& entry : m_config->read_entry("Launcher", "FileTypes").split(',')) {
  131. entry = entry.trim_whitespace();
  132. if (!entry.is_empty())
  133. file_types.append(entry);
  134. }
  135. return file_types;
  136. }
  137. Vector<ByteString> AppFile::launcher_protocols() const
  138. {
  139. Vector<ByteString> protocols;
  140. for (auto& entry : m_config->read_entry("Launcher", "Protocols").split(',')) {
  141. entry = entry.trim_whitespace();
  142. if (!entry.is_empty())
  143. protocols.append(entry);
  144. }
  145. return protocols;
  146. }
  147. bool AppFile::spawn(ReadonlySpan<StringView> arguments) const
  148. {
  149. if (!is_valid())
  150. return false;
  151. auto pid = Core::Process::spawn(executable(), arguments, working_directory());
  152. if (pid.is_error())
  153. return false;
  154. return true;
  155. }
  156. bool AppFile::spawn_with_escalation(ReadonlySpan<StringView> user_arguments) const
  157. {
  158. if (!is_valid())
  159. return false;
  160. StringView exe;
  161. Vector<StringView, 2> args;
  162. // FIXME: These single quotes won't be enough for executables with single quotes in their name.
  163. auto pls_with_executable = ByteString::formatted("/bin/pls '{}'", executable());
  164. if (run_in_terminal() && !requires_root()) {
  165. exe = "/bin/Terminal"sv;
  166. args = { "-e"sv, executable().view() };
  167. } else if (!run_in_terminal() && requires_root()) {
  168. exe = "/bin/Escalator"sv;
  169. args = { executable().view() };
  170. } else if (run_in_terminal() && requires_root()) {
  171. exe = "/bin/Terminal"sv;
  172. args = { "-e"sv, pls_with_executable.view() };
  173. } else {
  174. exe = executable().view();
  175. }
  176. args.extend(Vector(user_arguments));
  177. auto pid = Core::Process::spawn(exe, args.span(),
  178. working_directory().is_empty() ? Core::StandardPaths::home_directory() : working_directory());
  179. if (pid.is_error())
  180. return false;
  181. return true;
  182. }
  183. void AppFile::spawn_with_escalation_or_show_error(GUI::Window& window, ReadonlySpan<StringView> arguments) const
  184. {
  185. if (!spawn_with_escalation(arguments))
  186. GUI::MessageBox::show_error(&window, ByteString::formatted("Failed to spawn {} with escalation", executable()));
  187. }
  188. }