1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "nsGIOService.h"
7 #include "nsString.h"
8 #include "nsIURI.h"
9 #include "nsTArray.h"
10 #include "nsIStringEnumerator.h"
11 #include "nsAutoPtr.h"
12 #include "nsIMIMEInfo.h"
13 #include "nsComponentManagerUtils.h"
14 #include "nsArray.h"
15 #include "nsIFile.h"
16 #include "nsPrintfCString.h"
17 
18 #include <gio/gio.h>
19 #include <gtk/gtk.h>
20 #ifdef MOZ_ENABLE_DBUS
21 #include <dbus/dbus-glib.h>
22 #include <dbus/dbus-glib-lowlevel.h>
23 #endif
24 
25 // We use the same code as gtk_should_use_portal() to detect if we're in flatpak
26 // env
27 // https://github.com/GNOME/gtk/blob/e0ce028c88858b96aeda9e41734a39a3a04f705d/gtk/gtkprivate.c#L272
GetShouldUseFlatpakPortal()28 static bool GetShouldUseFlatpakPortal() {
29   bool shouldUsePortal;
30   char* path;
31   path = g_build_filename(g_get_user_runtime_dir(), "flatpak-info", nullptr);
32   if (g_file_test(path, G_FILE_TEST_EXISTS)) {
33     shouldUsePortal = true;
34   } else {
35     shouldUsePortal = (g_getenv("GTK_USE_PORTAL") != nullptr);
36   }
37   g_free(path);
38   return shouldUsePortal;
39 }
40 
ShouldUseFlatpakPortal()41 static bool ShouldUseFlatpakPortal() {
42   static bool sShouldUseFlatpakPortal = GetShouldUseFlatpakPortal();
43   return sShouldUseFlatpakPortal;
44 }
45 
46 class nsFlatpakHandlerApp : public nsIHandlerApp {
47  public:
48   NS_DECL_ISUPPORTS
49   NS_DECL_NSIHANDLERAPP
50   nsFlatpakHandlerApp() = default;
51 
52  private:
53   virtual ~nsFlatpakHandlerApp() = default;
54 };
55 
NS_IMPL_ISUPPORTS(nsFlatpakHandlerApp,nsIHandlerApp)56 NS_IMPL_ISUPPORTS(nsFlatpakHandlerApp, nsIHandlerApp)
57 
58 NS_IMETHODIMP
59 nsFlatpakHandlerApp::GetName(nsAString& aName) {
60   aName.AssignLiteral("System Handler");
61   return NS_OK;
62 }
63 
64 NS_IMETHODIMP
SetName(const nsAString & aName)65 nsFlatpakHandlerApp::SetName(const nsAString& aName) {
66   // We don't implement SetName because flatpak system handler name is fixed
67   return NS_OK;
68 }
69 
70 NS_IMETHODIMP
GetDetailedDescription(nsAString & aDetailedDescription)71 nsFlatpakHandlerApp::GetDetailedDescription(nsAString& aDetailedDescription) {
72   return NS_ERROR_NOT_IMPLEMENTED;
73 }
74 
75 NS_IMETHODIMP
SetDetailedDescription(const nsAString & aDetailedDescription)76 nsFlatpakHandlerApp::SetDetailedDescription(
77     const nsAString& aDetailedDescription) {
78   return NS_ERROR_NOT_IMPLEMENTED;
79 }
80 
81 NS_IMETHODIMP
Equals(nsIHandlerApp * aHandlerApp,bool * _retval)82 nsFlatpakHandlerApp::Equals(nsIHandlerApp* aHandlerApp, bool* _retval) {
83   return NS_ERROR_NOT_IMPLEMENTED;
84 }
85 
86 NS_IMETHODIMP
LaunchWithURI(nsIURI * aUri,nsIInterfaceRequestor * aRequestor)87 nsFlatpakHandlerApp::LaunchWithURI(nsIURI* aUri,
88                                    nsIInterfaceRequestor* aRequestor) {
89   nsCString spec;
90   aUri->GetSpec(spec);
91   GError* error = nullptr;
92 
93   // The TMPDIR where files are downloaded when user choose to open them
94   // needs to be accessible from sandbox and host. The default settings
95   // TMPDIR=/tmp is accessible only to the sandbox. That can be the reason
96   // why the gtk_show_uri fails there.
97   // The workaround is to set TMPDIR environment variable in sandbox to
98   // $XDG_CACHE_HOME/tmp before executing Firefox.
99   gtk_show_uri(nullptr, spec.get(), GDK_CURRENT_TIME, &error);
100   if (error) {
101     NS_WARNING(
102         nsPrintfCString("Cannot launch flatpak handler: %s", error->message)
103             .get());
104     g_error_free(error);
105     return NS_ERROR_FAILURE;
106   }
107   return NS_OK;
108 }
109 
110 /**
111  * Get command without any additional arguments
112  * @param aCommandWithArguments full commandline input string
113  * @param aCommand string for storing command without arguments
114  * @return NS_ERROR_FAILURE when unable to parse commandline
115  */
GetCommandFromCommandline(nsACString const & aCommandWithArguments,nsACString & aCommand)116 static nsresult GetCommandFromCommandline(
117     nsACString const& aCommandWithArguments, nsACString& aCommand) {
118   GError* error = nullptr;
119   gchar** argv = nullptr;
120   if (!g_shell_parse_argv(aCommandWithArguments.BeginReading(), nullptr, &argv,
121                           &error) ||
122       !argv[0]) {
123     g_warning("Cannot parse command with arguments: %s", error->message);
124     g_error_free(error);
125     g_strfreev(argv);
126     return NS_ERROR_FAILURE;
127   }
128   aCommand.Assign(argv[0]);
129   g_strfreev(argv);
130   return NS_OK;
131 }
132 
133 class nsGIOMimeApp final : public nsIGIOMimeApp {
134  public:
135   NS_DECL_ISUPPORTS
136   NS_DECL_NSIHANDLERAPP
137   NS_DECL_NSIGIOMIMEAPP
138 
nsGIOMimeApp(GAppInfo * aApp)139   explicit nsGIOMimeApp(GAppInfo* aApp) : mApp(aApp) {}
140 
141  private:
~nsGIOMimeApp()142   ~nsGIOMimeApp() { g_object_unref(mApp); }
143 
144   GAppInfo* mApp;
145 };
146 
NS_IMPL_ISUPPORTS(nsGIOMimeApp,nsIGIOMimeApp,nsIHandlerApp)147 NS_IMPL_ISUPPORTS(nsGIOMimeApp, nsIGIOMimeApp, nsIHandlerApp)
148 
149 NS_IMETHODIMP
150 nsGIOMimeApp::GetId(nsACString& aId) {
151   aId.Assign(g_app_info_get_id(mApp));
152   return NS_OK;
153 }
154 
155 NS_IMETHODIMP
GetName(nsAString & aName)156 nsGIOMimeApp::GetName(nsAString& aName) {
157   aName.Assign(NS_ConvertUTF8toUTF16(g_app_info_get_name(mApp)));
158   return NS_OK;
159 }
160 
161 NS_IMETHODIMP
SetName(const nsAString & aName)162 nsGIOMimeApp::SetName(const nsAString& aName) {
163   // We don't implement SetName because we're using mGIOMimeApp instance for
164   // obtaining application name
165   return NS_OK;
166 }
167 
168 NS_IMETHODIMP
GetCommand(nsACString & aCommand)169 nsGIOMimeApp::GetCommand(nsACString& aCommand) {
170   const char* cmd = g_app_info_get_commandline(mApp);
171   if (!cmd) return NS_ERROR_FAILURE;
172   aCommand.Assign(cmd);
173   return NS_OK;
174 }
175 
176 NS_IMETHODIMP
GetExpectsURIs(int32_t * aExpects)177 nsGIOMimeApp::GetExpectsURIs(int32_t* aExpects) {
178   *aExpects = g_app_info_supports_uris(mApp);
179   return NS_OK;
180 }
181 
182 NS_IMETHODIMP
GetDetailedDescription(nsAString & aDetailedDescription)183 nsGIOMimeApp::GetDetailedDescription(nsAString& aDetailedDescription) {
184   return NS_ERROR_NOT_IMPLEMENTED;
185 }
186 
187 NS_IMETHODIMP
SetDetailedDescription(const nsAString & aDetailedDescription)188 nsGIOMimeApp::SetDetailedDescription(const nsAString& aDetailedDescription) {
189   return NS_ERROR_NOT_IMPLEMENTED;
190 }
191 
192 NS_IMETHODIMP
Equals(nsIHandlerApp * aHandlerApp,bool * _retval)193 nsGIOMimeApp::Equals(nsIHandlerApp* aHandlerApp, bool* _retval) {
194   if (!aHandlerApp) return NS_ERROR_FAILURE;
195 
196   // Compare with nsILocalHandlerApp instance by name
197   nsCOMPtr<nsILocalHandlerApp> localHandlerApp = do_QueryInterface(aHandlerApp);
198   if (localHandlerApp) {
199     nsAutoString theirName;
200     nsAutoString thisName;
201     GetName(thisName);
202     localHandlerApp->GetName(theirName);
203     *_retval = thisName.Equals(theirName);
204     return NS_OK;
205   }
206 
207   // Compare with nsIGIOMimeApp instance by command with stripped arguments
208   nsCOMPtr<nsIGIOMimeApp> gioMimeApp = do_QueryInterface(aHandlerApp);
209   if (gioMimeApp) {
210     nsAutoCString thisCommandline, thisCommand;
211     nsresult rv = GetCommand(thisCommandline);
212     NS_ENSURE_SUCCESS(rv, rv);
213 
214     rv = GetCommandFromCommandline(thisCommandline, thisCommand);
215     NS_ENSURE_SUCCESS(rv, rv);
216 
217     nsAutoCString theirCommandline, theirCommand;
218     gioMimeApp->GetCommand(theirCommandline);
219     NS_ENSURE_SUCCESS(rv, rv);
220 
221     rv = GetCommandFromCommandline(theirCommandline, theirCommand);
222     NS_ENSURE_SUCCESS(rv, rv);
223 
224     *_retval = thisCommand.Equals(theirCommand);
225     return NS_OK;
226   }
227 
228   // We can only compare with nsILocalHandlerApp and nsGIOMimeApp
229   *_retval = false;
230   return NS_OK;
231 }
232 
233 NS_IMETHODIMP
LaunchWithURI(nsIURI * aUri,nsIInterfaceRequestor * aRequestor)234 nsGIOMimeApp::LaunchWithURI(nsIURI* aUri, nsIInterfaceRequestor* aRequestor) {
235   GList uris = {0};
236   nsCString spec;
237   aUri->GetSpec(spec);
238   // nsPromiseFlatCString flatUri(aUri);
239   uris.data = const_cast<char*>(spec.get());
240 
241   GError* error = nullptr;
242   gboolean result = g_app_info_launch_uris(mApp, &uris, nullptr, &error);
243 
244   if (!result) {
245     g_warning("Cannot launch application: %s", error->message);
246     g_error_free(error);
247     return NS_ERROR_FAILURE;
248   }
249 
250   return NS_OK;
251 }
252 
253 class GIOUTF8StringEnumerator final : public nsIUTF8StringEnumerator {
254   ~GIOUTF8StringEnumerator() = default;
255 
256  public:
GIOUTF8StringEnumerator()257   GIOUTF8StringEnumerator() : mIndex(0) {}
258 
259   NS_DECL_ISUPPORTS
260   NS_DECL_NSIUTF8STRINGENUMERATOR
261 
262   nsTArray<nsCString> mStrings;
263   uint32_t mIndex;
264 };
265 
NS_IMPL_ISUPPORTS(GIOUTF8StringEnumerator,nsIUTF8StringEnumerator)266 NS_IMPL_ISUPPORTS(GIOUTF8StringEnumerator, nsIUTF8StringEnumerator)
267 
268 NS_IMETHODIMP
269 GIOUTF8StringEnumerator::HasMore(bool* aResult) {
270   *aResult = mIndex < mStrings.Length();
271   return NS_OK;
272 }
273 
274 NS_IMETHODIMP
GetNext(nsACString & aResult)275 GIOUTF8StringEnumerator::GetNext(nsACString& aResult) {
276   if (mIndex >= mStrings.Length()) return NS_ERROR_UNEXPECTED;
277 
278   aResult.Assign(mStrings[mIndex]);
279   ++mIndex;
280   return NS_OK;
281 }
282 
283 NS_IMETHODIMP
GetSupportedURISchemes(nsIUTF8StringEnumerator ** aSchemes)284 nsGIOMimeApp::GetSupportedURISchemes(nsIUTF8StringEnumerator** aSchemes) {
285   *aSchemes = nullptr;
286 
287   RefPtr<GIOUTF8StringEnumerator> array = new GIOUTF8StringEnumerator();
288   NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
289 
290   GVfs* gvfs = g_vfs_get_default();
291 
292   if (!gvfs) {
293     g_warning("Cannot get GVfs object.");
294     return NS_ERROR_OUT_OF_MEMORY;
295   }
296 
297   const gchar* const* uri_schemes = g_vfs_get_supported_uri_schemes(gvfs);
298 
299   while (*uri_schemes != nullptr) {
300     if (!array->mStrings.AppendElement(*uri_schemes)) {
301       return NS_ERROR_OUT_OF_MEMORY;
302     }
303     uri_schemes++;
304   }
305 
306   array.forget(aSchemes);
307   return NS_OK;
308 }
309 
310 NS_IMETHODIMP
SetAsDefaultForMimeType(nsACString const & aMimeType)311 nsGIOMimeApp::SetAsDefaultForMimeType(nsACString const& aMimeType) {
312   char* content_type =
313       g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
314   if (!content_type) return NS_ERROR_FAILURE;
315   GError* error = nullptr;
316   g_app_info_set_as_default_for_type(mApp, content_type, &error);
317   if (error) {
318     g_warning("Cannot set application as default for MIME type (%s): %s",
319               PromiseFlatCString(aMimeType).get(), error->message);
320     g_error_free(error);
321     g_free(content_type);
322     return NS_ERROR_FAILURE;
323   }
324 
325   g_free(content_type);
326   return NS_OK;
327 }
328 /**
329  * Set default application for files with given extensions
330  * @param fileExts string of space separated extensions
331  * @return NS_OK when application was set as default for given extensions,
332  * NS_ERROR_FAILURE otherwise
333  */
334 NS_IMETHODIMP
SetAsDefaultForFileExtensions(nsACString const & fileExts)335 nsGIOMimeApp::SetAsDefaultForFileExtensions(nsACString const& fileExts) {
336   GError* error = nullptr;
337   char* extensions = g_strdup(PromiseFlatCString(fileExts).get());
338   char* ext_pos = extensions;
339   char* space_pos;
340 
341   while ((space_pos = strchr(ext_pos, ' ')) || (*ext_pos != '\0')) {
342     if (space_pos) {
343       *space_pos = '\0';
344     }
345     g_app_info_set_as_default_for_extension(mApp, ext_pos, &error);
346     if (error) {
347       g_warning("Cannot set application as default for extension (%s): %s",
348                 ext_pos, error->message);
349       g_error_free(error);
350       g_free(extensions);
351       return NS_ERROR_FAILURE;
352     }
353     if (space_pos) {
354       ext_pos = space_pos + 1;
355     } else {
356       *ext_pos = '\0';
357     }
358   }
359   g_free(extensions);
360   return NS_OK;
361 }
362 
363 /**
364  * Set default application for URI's of a particular scheme
365  * @param aURIScheme string containing the URI scheme
366  * @return NS_OK when application was set as default for URI scheme,
367  * NS_ERROR_FAILURE otherwise
368  */
369 NS_IMETHODIMP
SetAsDefaultForURIScheme(nsACString const & aURIScheme)370 nsGIOMimeApp::SetAsDefaultForURIScheme(nsACString const& aURIScheme) {
371   GError* error = nullptr;
372   nsAutoCString contentType("x-scheme-handler/");
373   contentType.Append(aURIScheme);
374 
375   g_app_info_set_as_default_for_type(mApp, contentType.get(), &error);
376   if (error) {
377     g_warning("Cannot set application as default for URI scheme (%s): %s",
378               PromiseFlatCString(aURIScheme).get(), error->message);
379     g_error_free(error);
380     return NS_ERROR_FAILURE;
381   }
382 
383   return NS_OK;
384 }
385 
NS_IMPL_ISUPPORTS(nsGIOService,nsIGIOService)386 NS_IMPL_ISUPPORTS(nsGIOService, nsIGIOService)
387 
388 NS_IMETHODIMP
389 nsGIOService::GetMimeTypeFromExtension(const nsACString& aExtension,
390                                        nsACString& aMimeType) {
391   nsAutoCString fileExtToUse("file.");
392   fileExtToUse.Append(aExtension);
393 
394   gboolean result_uncertain;
395   char* content_type =
396       g_content_type_guess(fileExtToUse.get(), nullptr, 0, &result_uncertain);
397   if (!content_type) return NS_ERROR_FAILURE;
398 
399   char* mime_type = g_content_type_get_mime_type(content_type);
400   if (!mime_type) {
401     g_free(content_type);
402     return NS_ERROR_FAILURE;
403   }
404 
405   aMimeType.Assign(mime_type);
406 
407   g_free(mime_type);
408   g_free(content_type);
409 
410   return NS_OK;
411 }
412 // used in nsGNOMERegistry
413 // -----------------------------------------------------------------------------
414 NS_IMETHODIMP
GetAppForURIScheme(const nsACString & aURIScheme,nsIHandlerApp ** aApp)415 nsGIOService::GetAppForURIScheme(const nsACString& aURIScheme,
416                                  nsIHandlerApp** aApp) {
417   *aApp = nullptr;
418 
419   // Application in flatpak sandbox does not have access to the list
420   // of installed applications on the system. We use generic
421   // nsFlatpakHandlerApp which forwards launch call to the system.
422   if (ShouldUseFlatpakPortal()) {
423     nsFlatpakHandlerApp* mozApp = new nsFlatpakHandlerApp();
424     NS_ADDREF(*aApp = mozApp);
425     return NS_OK;
426   }
427 
428   GAppInfo* app_info = g_app_info_get_default_for_uri_scheme(
429       PromiseFlatCString(aURIScheme).get());
430   if (app_info) {
431     nsGIOMimeApp* mozApp = new nsGIOMimeApp(app_info);
432     NS_ADDREF(*aApp = mozApp);
433   } else {
434     return NS_ERROR_FAILURE;
435   }
436   return NS_OK;
437 }
438 
439 NS_IMETHODIMP
GetAppsForURIScheme(const nsACString & aURIScheme,nsIMutableArray ** aResult)440 nsGIOService::GetAppsForURIScheme(const nsACString& aURIScheme,
441                                   nsIMutableArray** aResult) {
442   // We don't need to return the nsFlatpakHandlerApp here because
443   // it would be skipped by the callers anyway.
444   // The preferred handler is provided by GetAppForURIScheme.
445   // This method returns all possible application handlers
446   // including preferred one. The callers skips the preferred
447   // handler in this list to avoid duplicate records in the list
448   // they create.
449   nsCOMPtr<nsIMutableArray> handlersArray =
450       do_CreateInstance(NS_ARRAY_CONTRACTID);
451 
452   nsAutoCString contentType("x-scheme-handler/");
453   contentType.Append(aURIScheme);
454 
455   GList* appInfoList = g_app_info_get_all_for_type(contentType.get());
456   // g_app_info_get_all_for_type returns NULL when no appinfo is found
457   // or error occurs (contentType is NULL). We are fine with empty app list
458   // and we're sure that contentType is not NULL, so we won't return failure.
459   if (appInfoList) {
460     GList* appInfo = appInfoList;
461     while (appInfo) {
462       nsCOMPtr<nsIGIOMimeApp> mimeApp =
463           new nsGIOMimeApp(G_APP_INFO(appInfo->data));
464       handlersArray->AppendElement(mimeApp);
465       appInfo = appInfo->next;
466     }
467     g_list_free(appInfoList);
468   }
469   NS_ADDREF(*aResult = handlersArray);
470   return NS_OK;
471 }
472 
473 NS_IMETHODIMP
GetAppForMimeType(const nsACString & aMimeType,nsIHandlerApp ** aApp)474 nsGIOService::GetAppForMimeType(const nsACString& aMimeType,
475                                 nsIHandlerApp** aApp) {
476   *aApp = nullptr;
477 
478   // Flatpak does not reveal installed application to the sandbox,
479   // we need to create generic system handler.
480   if (ShouldUseFlatpakPortal()) {
481     nsFlatpakHandlerApp* mozApp = new nsFlatpakHandlerApp();
482     NS_ADDREF(*aApp = mozApp);
483     return NS_OK;
484   }
485 
486   char* content_type =
487       g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
488   if (!content_type) return NS_ERROR_FAILURE;
489 
490   GAppInfo* app_info = g_app_info_get_default_for_type(content_type, false);
491   if (app_info) {
492     nsGIOMimeApp* mozApp = new nsGIOMimeApp(app_info);
493     NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY);
494     NS_ADDREF(*aApp = mozApp);
495   } else {
496     g_free(content_type);
497     return NS_ERROR_FAILURE;
498   }
499   g_free(content_type);
500   return NS_OK;
501 }
502 
503 NS_IMETHODIMP
GetDescriptionForMimeType(const nsACString & aMimeType,nsACString & aDescription)504 nsGIOService::GetDescriptionForMimeType(const nsACString& aMimeType,
505                                         nsACString& aDescription) {
506   char* content_type =
507       g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
508   if (!content_type) return NS_ERROR_FAILURE;
509 
510   char* desc = g_content_type_get_description(content_type);
511   if (!desc) {
512     g_free(content_type);
513     return NS_ERROR_FAILURE;
514   }
515 
516   aDescription.Assign(desc);
517   g_free(content_type);
518   g_free(desc);
519   return NS_OK;
520 }
521 
522 NS_IMETHODIMP
ShowURI(nsIURI * aURI)523 nsGIOService::ShowURI(nsIURI* aURI) {
524   nsAutoCString spec;
525   nsresult rv = aURI->GetSpec(spec);
526   NS_ENSURE_SUCCESS(rv, rv);
527   GError* error = nullptr;
528   if (!g_app_info_launch_default_for_uri(spec.get(), nullptr, &error)) {
529     g_warning("Could not launch default application for URI: %s",
530               error->message);
531     g_error_free(error);
532     return NS_ERROR_FAILURE;
533   }
534   return NS_OK;
535 }
536 
537 NS_IMETHODIMP
ShowURIForInput(const nsACString & aUri)538 nsGIOService::ShowURIForInput(const nsACString& aUri) {
539   GFile* file = g_file_new_for_commandline_arg(PromiseFlatCString(aUri).get());
540   char* spec = g_file_get_uri(file);
541   nsresult rv = NS_ERROR_FAILURE;
542   GError* error = nullptr;
543 
544   g_app_info_launch_default_for_uri(spec, nullptr, &error);
545   if (error) {
546     g_warning("Cannot launch default application: %s", error->message);
547     g_error_free(error);
548   } else {
549     rv = NS_OK;
550   }
551   g_object_unref(file);
552   g_free(spec);
553 
554   return rv;
555 }
556 
557 NS_IMETHODIMP
OrgFreedesktopFileManager1ShowItems(const nsACString & aPath)558 nsGIOService::OrgFreedesktopFileManager1ShowItems(const nsACString& aPath) {
559 #ifndef MOZ_ENABLE_DBUS
560   return NS_ERROR_FAILURE;
561 #else
562   GError* error = nullptr;
563   static bool org_freedesktop_FileManager1_exists = true;
564 
565   if (!org_freedesktop_FileManager1_exists) {
566     return NS_ERROR_NOT_AVAILABLE;
567   }
568 
569   DBusGConnection* dbusGConnection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
570 
571   if (!dbusGConnection) {
572     if (error) {
573       g_printerr("Failed to open connection to session bus: %s\n",
574                  error->message);
575       g_error_free(error);
576     }
577     return NS_ERROR_FAILURE;
578   }
579 
580   char* uri =
581       g_filename_to_uri(PromiseFlatCString(aPath).get(), nullptr, nullptr);
582   if (uri == nullptr) {
583     return NS_ERROR_FAILURE;
584   }
585 
586   DBusConnection* dbusConnection =
587       dbus_g_connection_get_connection(dbusGConnection);
588   // Make sure we do not exit the entire program if DBus connection get lost.
589   dbus_connection_set_exit_on_disconnect(dbusConnection, false);
590 
591   DBusGProxy* dbusGProxy = dbus_g_proxy_new_for_name(
592       dbusGConnection, "org.freedesktop.FileManager1",
593       "/org/freedesktop/FileManager1", "org.freedesktop.FileManager1");
594 
595   const char* uris[2] = {uri, nullptr};
596   gboolean rv_dbus_call =
597       dbus_g_proxy_call(dbusGProxy, "ShowItems", nullptr, G_TYPE_STRV, uris,
598                         G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID);
599 
600   g_object_unref(dbusGProxy);
601   dbus_g_connection_unref(dbusGConnection);
602   g_free(uri);
603 
604   if (!rv_dbus_call) {
605     org_freedesktop_FileManager1_exists = false;
606     return NS_ERROR_NOT_AVAILABLE;
607   }
608 
609   return NS_OK;
610 #endif
611 }
612 
613 /**
614  * Find GIO Mime App from given commandline.
615  * This is different from CreateAppFromCommand because instead of creating the
616  * GIO Mime App in case it's not found in the GIO application list, the method
617  * returns error.
618  * @param aCmd command with parameters used to start the application
619  * @return NS_OK when application is found, NS_ERROR_NOT_AVAILABLE otherwise
620  */
621 NS_IMETHODIMP
FindAppFromCommand(nsACString const & aCmd,nsIGIOMimeApp ** aAppInfo)622 nsGIOService::FindAppFromCommand(nsACString const& aCmd,
623                                  nsIGIOMimeApp** aAppInfo) {
624   GAppInfo *app_info = nullptr, *app_info_from_list = nullptr;
625   GList* apps = g_app_info_get_all();
626   GList* apps_p = apps;
627 
628   // Try to find relevant and existing GAppInfo in all installed application
629   // We do this by comparing each GAppInfo's executable with out own
630   while (apps_p) {
631     app_info_from_list = (GAppInfo*)apps_p->data;
632     if (!app_info) {
633       // If the executable is not absolute, get it's full path
634       char* executable =
635           g_find_program_in_path(g_app_info_get_executable(app_info_from_list));
636 
637       if (executable &&
638           strcmp(executable, PromiseFlatCString(aCmd).get()) == 0) {
639         g_object_ref(app_info_from_list);
640         app_info = app_info_from_list;
641       }
642       g_free(executable);
643     }
644 
645     g_object_unref(app_info_from_list);
646     apps_p = apps_p->next;
647   }
648   g_list_free(apps);
649   if (app_info) {
650     nsGIOMimeApp* app = new nsGIOMimeApp(app_info);
651     NS_ENSURE_TRUE(app, NS_ERROR_OUT_OF_MEMORY);
652     NS_ADDREF(*aAppInfo = app);
653     return NS_OK;
654   }
655 
656   *aAppInfo = nullptr;
657   return NS_ERROR_NOT_AVAILABLE;
658 }
659 
660 /**
661  * Create application info for specified command and application name.
662  * Command arguments are ignored and the "%u" is always added.
663  * @param cmd command to execute
664  * @param appName application name
665  * @param appInfo location where created GAppInfo is stored
666  * @return NS_OK when object is created, NS_ERROR_FILE_NOT_FOUND when executable
667  * is not found in the system path or NS_ERROR_FAILURE otherwise.
668  */
669 NS_IMETHODIMP
CreateAppFromCommand(nsACString const & cmd,nsACString const & appName,nsIGIOMimeApp ** appInfo)670 nsGIOService::CreateAppFromCommand(nsACString const& cmd,
671                                    nsACString const& appName,
672                                    nsIGIOMimeApp** appInfo) {
673   GError* error = nullptr;
674   *appInfo = nullptr;
675 
676   // Using G_APP_INFO_CREATE_SUPPORTS_URIS calling
677   // g_app_info_create_from_commandline appends %u to the cmd even when cmd
678   // already contains this parameter. To avoid that we're going to remove
679   // arguments before passing to it.
680   nsAutoCString commandWithoutArgs;
681   nsresult rv = GetCommandFromCommandline(cmd, commandWithoutArgs);
682   NS_ENSURE_SUCCESS(rv, rv);
683   GAppInfo* app_info = g_app_info_create_from_commandline(
684       commandWithoutArgs.BeginReading(), PromiseFlatCString(appName).get(),
685       G_APP_INFO_CREATE_SUPPORTS_URIS, &error);
686   if (!app_info) {
687     g_warning("Cannot create application info from command: %s",
688               error->message);
689     g_error_free(error);
690     return NS_ERROR_FAILURE;
691   }
692 
693   // Check if executable exist in path
694   gchar* executableWithFullPath =
695       g_find_program_in_path(commandWithoutArgs.BeginReading());
696   if (!executableWithFullPath) {
697     return NS_ERROR_FILE_NOT_FOUND;
698   }
699   g_free(executableWithFullPath);
700 
701   nsGIOMimeApp* mozApp = new nsGIOMimeApp(app_info);
702   NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY);
703   NS_ADDREF(*appInfo = mozApp);
704   return NS_OK;
705 }
706