xref: /aosp_15_r20/external/cronet/base/nix/xdg_util.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/nix/xdg_util.h"
6 
7 #include <optional>
8 #include <string>
9 
10 #include "base/base_paths.h"
11 #include "base/command_line.h"
12 #include "base/environment.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/logging.h"
16 #include "base/no_destructor.h"
17 #include "base/path_service.h"
18 #include "base/process/launch.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_tokenizer.h"
21 #include "base/strings/string_util.h"
22 #include "base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h"
23 #include "base/threading/scoped_blocking_call.h"
24 
25 namespace {
26 
27 // The KDE session version environment variable introduced in KDE 4.
28 const char kKDESessionEnvVar[] = "KDE_SESSION_VERSION";
29 
GetXdgActivationTokenCreator()30 base::nix::XdgActivationTokenCreator& GetXdgActivationTokenCreator() {
31   static base::NoDestructor<base::nix::XdgActivationTokenCreator> creator;
32   return *creator;
33 }
34 
GetXdgActivationToken()35 std::optional<std::string>& GetXdgActivationToken() {
36   static base::NoDestructor<std::optional<std::string>> token;
37   return *token;
38 }
39 
40 }  // namespace
41 
42 namespace base::nix {
43 
44 const char kDotConfigDir[] = ".config";
45 const char kXdgConfigHomeEnvVar[] = "XDG_CONFIG_HOME";
46 const char kXdgCurrentDesktopEnvVar[] = "XDG_CURRENT_DESKTOP";
47 const char kXdgSessionTypeEnvVar[] = "XDG_SESSION_TYPE";
48 const char kXdgActivationTokenEnvVar[] = "XDG_ACTIVATION_TOKEN";
49 const char kXdgActivationTokenSwitch[] = "xdg-activation-token";
50 
GetXDGDirectory(Environment * env,const char * env_name,const char * fallback_dir)51 FilePath GetXDGDirectory(Environment* env,
52                          const char* env_name,
53                          const char* fallback_dir) {
54   FilePath path;
55   std::string env_value;
56   if (env->GetVar(env_name, &env_value) && !env_value.empty()) {
57     path = FilePath(env_value);
58   } else {
59     PathService::Get(DIR_HOME, &path);
60     path = path.Append(fallback_dir);
61   }
62   return path.StripTrailingSeparators();
63 }
64 
GetXDGUserDirectory(const char * dir_name,const char * fallback_dir)65 FilePath GetXDGUserDirectory(const char* dir_name, const char* fallback_dir) {
66   FilePath path;
67   char* xdg_dir = xdg_user_dir_lookup(dir_name);
68   if (xdg_dir) {
69     path = FilePath(xdg_dir);
70     free(xdg_dir);
71   } else {
72     PathService::Get(DIR_HOME, &path);
73     path = path.Append(fallback_dir);
74   }
75   return path.StripTrailingSeparators();
76 }
77 
GetXDGDataWriteLocation(Environment * env)78 FilePath GetXDGDataWriteLocation(Environment* env) {
79   return GetXDGDirectory(env, "XDG_DATA_HOME", ".local/share");
80 }
81 
GetXDGDataSearchLocations(Environment * env)82 std::vector<FilePath> GetXDGDataSearchLocations(Environment* env) {
83   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
84 
85   std::vector<FilePath> search_paths;
86   search_paths.push_back(GetXDGDataWriteLocation(env));
87 
88   std::string xdg_data_dirs;
89   if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && !xdg_data_dirs.empty()) {
90     StringTokenizer tokenizer(xdg_data_dirs, ":");
91     while (tokenizer.GetNext()) {
92       search_paths.emplace_back(tokenizer.token_piece());
93     }
94   } else {
95     search_paths.emplace_back("/usr/local/share");
96     search_paths.emplace_back("/usr/share");
97   }
98 
99   return search_paths;
100 }
101 
GetDesktopEnvironment(Environment * env)102 DesktopEnvironment GetDesktopEnvironment(Environment* env) {
103   // kXdgCurrentDesktopEnvVar is the newest standard circa 2012.
104   std::string xdg_current_desktop;
105   if (env->GetVar(kXdgCurrentDesktopEnvVar, &xdg_current_desktop)) {
106     // It could have multiple values separated by colon in priority order.
107     for (const auto& value : SplitStringPiece(
108              xdg_current_desktop, ":", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
109       if (value == "Unity") {
110         // gnome-fallback sessions set kXdgCurrentDesktopEnvVar to Unity
111         // DESKTOP_SESSION can be gnome-fallback or gnome-fallback-compiz
112         std::string desktop_session;
113         if (env->GetVar("DESKTOP_SESSION", &desktop_session) &&
114             desktop_session.find("gnome-fallback") != std::string::npos) {
115           return DESKTOP_ENVIRONMENT_GNOME;
116         }
117         return DESKTOP_ENVIRONMENT_UNITY;
118       }
119       if (value == "Deepin") {
120         return DESKTOP_ENVIRONMENT_DEEPIN;
121       }
122       if (value == "GNOME") {
123         return DESKTOP_ENVIRONMENT_GNOME;
124       }
125       if (value == "X-Cinnamon") {
126         return DESKTOP_ENVIRONMENT_CINNAMON;
127       }
128       if (value == "KDE") {
129         std::string kde_session;
130         if (env->GetVar(kKDESessionEnvVar, &kde_session)) {
131           if (kde_session == "5") {
132             return DESKTOP_ENVIRONMENT_KDE5;
133           }
134           if (kde_session == "6") {
135             return DESKTOP_ENVIRONMENT_KDE6;
136           }
137         }
138         return DESKTOP_ENVIRONMENT_KDE4;
139       }
140       if (value == "Pantheon") {
141         return DESKTOP_ENVIRONMENT_PANTHEON;
142       }
143       if (value == "XFCE") {
144         return DESKTOP_ENVIRONMENT_XFCE;
145       }
146       if (value == "UKUI") {
147         return DESKTOP_ENVIRONMENT_UKUI;
148       }
149       if (value == "LXQt") {
150         return DESKTOP_ENVIRONMENT_LXQT;
151       }
152     }
153   }
154 
155   // DESKTOP_SESSION was what everyone used in 2010.
156   std::string desktop_session;
157   if (env->GetVar("DESKTOP_SESSION", &desktop_session)) {
158     if (desktop_session == "deepin") {
159       return DESKTOP_ENVIRONMENT_DEEPIN;
160     }
161     if (desktop_session == "gnome" || desktop_session == "mate") {
162       return DESKTOP_ENVIRONMENT_GNOME;
163     }
164     if (desktop_session == "kde4" || desktop_session == "kde-plasma") {
165       return DESKTOP_ENVIRONMENT_KDE4;
166     }
167     if (desktop_session == "kde") {
168       // This may mean KDE4 on newer systems, so we have to check.
169       if (env->HasVar(kKDESessionEnvVar)) {
170         return DESKTOP_ENVIRONMENT_KDE4;
171       }
172       return DESKTOP_ENVIRONMENT_KDE3;
173     }
174     if (desktop_session.find("xfce") != std::string::npos ||
175         desktop_session == "xubuntu") {
176       return DESKTOP_ENVIRONMENT_XFCE;
177     }
178     if (desktop_session == "ukui") {
179       return DESKTOP_ENVIRONMENT_UKUI;
180     }
181   }
182 
183   // Fall back on some older environment variables.
184   // Useful particularly in the DESKTOP_SESSION=default case.
185   if (env->HasVar("GNOME_DESKTOP_SESSION_ID")) {
186     return DESKTOP_ENVIRONMENT_GNOME;
187   }
188   if (env->HasVar("KDE_FULL_SESSION")) {
189     if (env->HasVar(kKDESessionEnvVar)) {
190       return DESKTOP_ENVIRONMENT_KDE4;
191     }
192     return DESKTOP_ENVIRONMENT_KDE3;
193   }
194 
195   return DESKTOP_ENVIRONMENT_OTHER;
196 }
197 
GetDesktopEnvironmentName(DesktopEnvironment env)198 const char* GetDesktopEnvironmentName(DesktopEnvironment env) {
199   switch (env) {
200     case DESKTOP_ENVIRONMENT_OTHER:
201       return nullptr;
202     case DESKTOP_ENVIRONMENT_CINNAMON:
203       return "CINNAMON";
204     case DESKTOP_ENVIRONMENT_DEEPIN:
205       return "DEEPIN";
206     case DESKTOP_ENVIRONMENT_GNOME:
207       return "GNOME";
208     case DESKTOP_ENVIRONMENT_KDE3:
209       return "KDE3";
210     case DESKTOP_ENVIRONMENT_KDE4:
211       return "KDE4";
212     case DESKTOP_ENVIRONMENT_KDE5:
213       return "KDE5";
214     case DESKTOP_ENVIRONMENT_KDE6:
215       return "KDE6";
216     case DESKTOP_ENVIRONMENT_PANTHEON:
217       return "PANTHEON";
218     case DESKTOP_ENVIRONMENT_UNITY:
219       return "UNITY";
220     case DESKTOP_ENVIRONMENT_XFCE:
221       return "XFCE";
222     case DESKTOP_ENVIRONMENT_UKUI:
223       return "UKUI";
224     case DESKTOP_ENVIRONMENT_LXQT:
225       return "LXQT";
226   }
227   return nullptr;
228 }
229 
GetDesktopEnvironmentName(Environment * env)230 const char* GetDesktopEnvironmentName(Environment* env) {
231   return GetDesktopEnvironmentName(GetDesktopEnvironment(env));
232 }
233 
GetSessionType(Environment & env)234 SessionType GetSessionType(Environment& env) {
235   std::string xdg_session_type;
236   if (!env.GetVar(kXdgSessionTypeEnvVar, &xdg_session_type)) {
237     return SessionType::kUnset;
238   }
239 
240   TrimWhitespaceASCII(ToLowerASCII(xdg_session_type), TrimPositions::TRIM_ALL,
241                       &xdg_session_type);
242 
243   if (xdg_session_type == "wayland") {
244     return SessionType::kWayland;
245   }
246 
247   if (xdg_session_type == "x11") {
248     return SessionType::kX11;
249   }
250 
251   if (xdg_session_type == "tty") {
252     return SessionType::kTty;
253   }
254 
255   if (xdg_session_type == "mir") {
256     return SessionType::kMir;
257   }
258 
259   if (xdg_session_type == "unspecified") {
260     return SessionType::kUnspecified;
261   }
262 
263   LOG(ERROR) << "Unknown XDG_SESSION_TYPE: " << xdg_session_type;
264   return SessionType::kOther;
265 }
266 
ExtractXdgActivationTokenFromEnv(Environment & env)267 std::optional<std::string> ExtractXdgActivationTokenFromEnv(Environment& env) {
268   std::string token;
269   if (env.GetVar(kXdgActivationTokenEnvVar, &token) && !token.empty()) {
270     GetXdgActivationToken() = std::move(token);
271     env.UnSetVar(kXdgActivationTokenEnvVar);
272   }
273   return GetXdgActivationToken();
274 }
275 
ExtractXdgActivationTokenFromCmdLine(base::CommandLine & cmd_line)276 void ExtractXdgActivationTokenFromCmdLine(base::CommandLine& cmd_line) {
277   std::string token = cmd_line.GetSwitchValueASCII(kXdgActivationTokenSwitch);
278   if (!token.empty()) {
279     GetXdgActivationToken() = std::move(token);
280     cmd_line.RemoveSwitch(kXdgActivationTokenSwitch);
281   }
282 }
283 
TakeXdgActivationToken()284 std::optional<std::string> TakeXdgActivationToken() {
285   auto token = GetXdgActivationToken();
286   GetXdgActivationToken().reset();
287   return token;
288 }
289 
SetXdgActivationTokenCreator(XdgActivationTokenCreator token_creator)290 void SetXdgActivationTokenCreator(XdgActivationTokenCreator token_creator) {
291   GetXdgActivationTokenCreator() = std::move(token_creator);
292 }
293 
CreateLaunchOptionsWithXdgActivation(XdgActivationLaunchOptionsCallback callback)294 void CreateLaunchOptionsWithXdgActivation(
295     XdgActivationLaunchOptionsCallback callback) {
296   if (!GetXdgActivationTokenCreator()) {
297     // There is no token creator, so return an empty LaunchOptions.
298     std::move(callback).Run(LaunchOptions());
299     return;
300   }
301   auto create_token_cb =
302       [](XdgActivationLaunchOptionsCallback launch_options_cb,
303          std::string token) {
304         base::LaunchOptions options;
305         if (!token.empty()) {
306           options.environment[kXdgActivationTokenEnvVar] = token;
307         }
308         std::move(launch_options_cb).Run(options);
309       };
310   GetXdgActivationTokenCreator().Run(
311       base::BindOnce(create_token_cb, std::move(callback)));
312 }
313 
314 }  // namespace base::nix
315