Line data Source code
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 :
13 : #include <gio/gio.h>
14 : #include <gtk/gtk.h>
15 : #ifdef MOZ_ENABLE_DBUS
16 : #include <dbus/dbus-glib.h>
17 : #include <dbus/dbus-glib-lowlevel.h>
18 : #endif
19 :
20 :
21 : class nsGIOMimeApp final : public nsIGIOMimeApp
22 : {
23 : public:
24 : NS_DECL_ISUPPORTS
25 : NS_DECL_NSIGIOMIMEAPP
26 :
27 4 : explicit nsGIOMimeApp(GAppInfo* aApp) : mApp(aApp) {}
28 :
29 : private:
30 4 : ~nsGIOMimeApp() { g_object_unref(mApp); }
31 :
32 : GAppInfo *mApp;
33 : };
34 :
35 24 : NS_IMPL_ISUPPORTS(nsGIOMimeApp, nsIGIOMimeApp)
36 :
37 : NS_IMETHODIMP
38 0 : nsGIOMimeApp::GetId(nsACString& aId)
39 : {
40 0 : aId.Assign(g_app_info_get_id(mApp));
41 0 : return NS_OK;
42 : }
43 :
44 : NS_IMETHODIMP
45 4 : nsGIOMimeApp::GetName(nsACString& aName)
46 : {
47 4 : aName.Assign(g_app_info_get_name(mApp));
48 4 : return NS_OK;
49 : }
50 :
51 : NS_IMETHODIMP
52 0 : nsGIOMimeApp::GetCommand(nsACString& aCommand)
53 : {
54 0 : const char *cmd = g_app_info_get_commandline(mApp);
55 0 : if (!cmd)
56 0 : return NS_ERROR_FAILURE;
57 0 : aCommand.Assign(cmd);
58 0 : return NS_OK;
59 : }
60 :
61 : NS_IMETHODIMP
62 0 : nsGIOMimeApp::GetExpectsURIs(int32_t* aExpects)
63 : {
64 0 : *aExpects = g_app_info_supports_uris(mApp);
65 0 : return NS_OK;
66 : }
67 :
68 : NS_IMETHODIMP
69 0 : nsGIOMimeApp::Launch(const nsACString& aUri)
70 : {
71 0 : GList uris = { 0 };
72 0 : nsPromiseFlatCString flatUri(aUri);
73 0 : uris.data = const_cast<char*>(flatUri.get());
74 :
75 0 : GError *error = nullptr;
76 0 : gboolean result = g_app_info_launch_uris(mApp, &uris, nullptr, &error);
77 :
78 0 : if (!result) {
79 0 : g_warning("Cannot launch application: %s", error->message);
80 0 : g_error_free(error);
81 0 : return NS_ERROR_FAILURE;
82 : }
83 :
84 0 : return NS_OK;
85 : }
86 :
87 : class GIOUTF8StringEnumerator final : public nsIUTF8StringEnumerator
88 : {
89 0 : ~GIOUTF8StringEnumerator() = default;
90 :
91 : public:
92 0 : GIOUTF8StringEnumerator() : mIndex(0) { }
93 :
94 : NS_DECL_ISUPPORTS
95 : NS_DECL_NSIUTF8STRINGENUMERATOR
96 :
97 : nsTArray<nsCString> mStrings;
98 : uint32_t mIndex;
99 : };
100 :
101 0 : NS_IMPL_ISUPPORTS(GIOUTF8StringEnumerator, nsIUTF8StringEnumerator)
102 :
103 : NS_IMETHODIMP
104 0 : GIOUTF8StringEnumerator::HasMore(bool* aResult)
105 : {
106 0 : *aResult = mIndex < mStrings.Length();
107 0 : return NS_OK;
108 : }
109 :
110 : NS_IMETHODIMP
111 0 : GIOUTF8StringEnumerator::GetNext(nsACString& aResult)
112 : {
113 0 : if (mIndex >= mStrings.Length())
114 0 : return NS_ERROR_UNEXPECTED;
115 :
116 0 : aResult.Assign(mStrings[mIndex]);
117 0 : ++mIndex;
118 0 : return NS_OK;
119 : }
120 :
121 : NS_IMETHODIMP
122 0 : nsGIOMimeApp::GetSupportedURISchemes(nsIUTF8StringEnumerator** aSchemes)
123 : {
124 0 : *aSchemes = nullptr;
125 :
126 0 : RefPtr<GIOUTF8StringEnumerator> array = new GIOUTF8StringEnumerator();
127 0 : NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
128 :
129 0 : GVfs *gvfs = g_vfs_get_default();
130 :
131 0 : if (!gvfs) {
132 0 : g_warning("Cannot get GVfs object.");
133 0 : return NS_ERROR_OUT_OF_MEMORY;
134 : }
135 :
136 0 : const gchar* const * uri_schemes = g_vfs_get_supported_uri_schemes(gvfs);
137 :
138 0 : while (*uri_schemes != nullptr) {
139 0 : if (!array->mStrings.AppendElement(*uri_schemes)) {
140 0 : return NS_ERROR_OUT_OF_MEMORY;
141 : }
142 0 : uri_schemes++;
143 : }
144 :
145 0 : array.forget(aSchemes);
146 0 : return NS_OK;
147 : }
148 :
149 : NS_IMETHODIMP
150 0 : nsGIOMimeApp::SetAsDefaultForMimeType(nsACString const& aMimeType)
151 : {
152 : char *content_type =
153 0 : g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
154 0 : if (!content_type)
155 0 : return NS_ERROR_FAILURE;
156 0 : GError *error = nullptr;
157 0 : g_app_info_set_as_default_for_type(mApp,
158 : content_type,
159 0 : &error);
160 0 : if (error) {
161 0 : g_warning("Cannot set application as default for MIME type (%s): %s",
162 : PromiseFlatCString(aMimeType).get(),
163 0 : error->message);
164 0 : g_error_free(error);
165 0 : g_free(content_type);
166 0 : return NS_ERROR_FAILURE;
167 : }
168 :
169 0 : g_free(content_type);
170 0 : return NS_OK;
171 : }
172 : /**
173 : * Set default application for files with given extensions
174 : * @param fileExts string of space separated extensions
175 : * @return NS_OK when application was set as default for given extensions,
176 : * NS_ERROR_FAILURE otherwise
177 : */
178 : NS_IMETHODIMP
179 0 : nsGIOMimeApp::SetAsDefaultForFileExtensions(nsACString const& fileExts)
180 : {
181 0 : GError *error = nullptr;
182 0 : char *extensions = g_strdup(PromiseFlatCString(fileExts).get());
183 0 : char *ext_pos = extensions;
184 : char *space_pos;
185 :
186 0 : while ( (space_pos = strchr(ext_pos, ' ')) || (*ext_pos != '\0') ) {
187 0 : if (space_pos) {
188 0 : *space_pos = '\0';
189 : }
190 0 : g_app_info_set_as_default_for_extension(mApp, ext_pos, &error);
191 0 : if (error) {
192 0 : g_warning("Cannot set application as default for extension (%s): %s",
193 : ext_pos,
194 0 : error->message);
195 0 : g_error_free(error);
196 0 : g_free(extensions);
197 0 : return NS_ERROR_FAILURE;
198 : }
199 0 : if (space_pos) {
200 0 : ext_pos = space_pos + 1;
201 : } else {
202 0 : *ext_pos = '\0';
203 : }
204 : }
205 0 : g_free(extensions);
206 0 : return NS_OK;
207 : }
208 :
209 : /**
210 : * Set default application for URI's of a particular scheme
211 : * @param aURIScheme string containing the URI scheme
212 : * @return NS_OK when application was set as default for URI scheme,
213 : * NS_ERROR_FAILURE otherwise
214 : */
215 : NS_IMETHODIMP
216 0 : nsGIOMimeApp::SetAsDefaultForURIScheme(nsACString const& aURIScheme)
217 : {
218 0 : GError *error = nullptr;
219 0 : nsAutoCString contentType("x-scheme-handler/");
220 0 : contentType.Append(aURIScheme);
221 :
222 0 : g_app_info_set_as_default_for_type(mApp,
223 : contentType.get(),
224 0 : &error);
225 0 : if (error) {
226 0 : g_warning("Cannot set application as default for URI scheme (%s): %s",
227 : PromiseFlatCString(aURIScheme).get(),
228 0 : error->message);
229 0 : g_error_free(error);
230 0 : return NS_ERROR_FAILURE;
231 : }
232 :
233 0 : return NS_OK;
234 : }
235 :
236 15 : NS_IMPL_ISUPPORTS(nsGIOService, nsIGIOService)
237 :
238 : NS_IMETHODIMP
239 0 : nsGIOService::GetMimeTypeFromExtension(const nsACString& aExtension,
240 : nsACString& aMimeType)
241 : {
242 0 : nsAutoCString fileExtToUse("file.");
243 0 : fileExtToUse.Append(aExtension);
244 :
245 : gboolean result_uncertain;
246 0 : char *content_type = g_content_type_guess(fileExtToUse.get(),
247 : nullptr,
248 : 0,
249 0 : &result_uncertain);
250 0 : if (!content_type)
251 0 : return NS_ERROR_FAILURE;
252 :
253 0 : char *mime_type = g_content_type_get_mime_type(content_type);
254 0 : if (!mime_type) {
255 0 : g_free(content_type);
256 0 : return NS_ERROR_FAILURE;
257 : }
258 :
259 0 : aMimeType.Assign(mime_type);
260 :
261 0 : g_free(mime_type);
262 0 : g_free(content_type);
263 :
264 0 : return NS_OK;
265 : }
266 : // used in nsGNOMERegistry
267 : // -----------------------------------------------------------------------------
268 : NS_IMETHODIMP
269 0 : nsGIOService::GetAppForURIScheme(const nsACString& aURIScheme,
270 : nsIGIOMimeApp** aApp)
271 : {
272 0 : *aApp = nullptr;
273 :
274 0 : GAppInfo *app_info = g_app_info_get_default_for_uri_scheme(
275 0 : PromiseFlatCString(aURIScheme).get());
276 0 : if (app_info) {
277 0 : nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info);
278 0 : NS_ADDREF(*aApp = mozApp);
279 : } else {
280 0 : return NS_ERROR_FAILURE;
281 : }
282 0 : return NS_OK;
283 : }
284 :
285 : NS_IMETHODIMP
286 4 : nsGIOService::GetAppForMimeType(const nsACString& aMimeType,
287 : nsIGIOMimeApp** aApp)
288 : {
289 4 : *aApp = nullptr;
290 : char *content_type =
291 4 : g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
292 4 : if (!content_type)
293 0 : return NS_ERROR_FAILURE;
294 :
295 4 : GAppInfo *app_info = g_app_info_get_default_for_type(content_type, false);
296 4 : if (app_info) {
297 4 : nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info);
298 4 : NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY);
299 4 : NS_ADDREF(*aApp = mozApp);
300 : } else {
301 0 : g_free(content_type);
302 0 : return NS_ERROR_FAILURE;
303 : }
304 4 : g_free(content_type);
305 4 : return NS_OK;
306 : }
307 :
308 : NS_IMETHODIMP
309 4 : nsGIOService::GetDescriptionForMimeType(const nsACString& aMimeType,
310 : nsACString& aDescription)
311 : {
312 : char *content_type =
313 4 : g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get());
314 4 : if (!content_type)
315 0 : return NS_ERROR_FAILURE;
316 :
317 4 : char *desc = g_content_type_get_description(content_type);
318 4 : if (!desc) {
319 0 : g_free(content_type);
320 0 : return NS_ERROR_FAILURE;
321 : }
322 :
323 4 : aDescription.Assign(desc);
324 4 : g_free(content_type);
325 4 : g_free(desc);
326 4 : return NS_OK;
327 : }
328 :
329 : NS_IMETHODIMP
330 0 : nsGIOService::ShowURI(nsIURI* aURI)
331 : {
332 0 : nsAutoCString spec;
333 0 : nsresult rv = aURI->GetSpec(spec);
334 0 : NS_ENSURE_SUCCESS(rv, rv);
335 0 : GError *error = nullptr;
336 0 : if (!g_app_info_launch_default_for_uri(spec.get(), nullptr, &error)) {
337 0 : g_warning("Could not launch default application for URI: %s", error->message);
338 0 : g_error_free(error);
339 0 : return NS_ERROR_FAILURE;
340 : }
341 0 : return NS_OK;
342 : }
343 :
344 : NS_IMETHODIMP
345 0 : nsGIOService::ShowURIForInput(const nsACString& aUri)
346 : {
347 0 : GFile *file = g_file_new_for_commandline_arg(PromiseFlatCString(aUri).get());
348 0 : char* spec = g_file_get_uri(file);
349 0 : nsresult rv = NS_ERROR_FAILURE;
350 0 : GError *error = nullptr;
351 :
352 0 : g_app_info_launch_default_for_uri(spec, nullptr, &error);
353 0 : if (error) {
354 0 : g_warning("Cannot launch default application: %s", error->message);
355 0 : g_error_free(error);
356 : } else {
357 0 : rv = NS_OK;
358 : }
359 0 : g_object_unref(file);
360 0 : g_free(spec);
361 :
362 0 : return rv;
363 : }
364 :
365 : NS_IMETHODIMP
366 0 : nsGIOService::OrgFreedesktopFileManager1ShowItems(const nsACString& aPath)
367 : {
368 : #ifndef MOZ_ENABLE_DBUS
369 : return NS_ERROR_FAILURE;
370 : #else
371 0 : GError* error = nullptr;
372 : static bool org_freedesktop_FileManager1_exists = true;
373 :
374 0 : if (!org_freedesktop_FileManager1_exists) {
375 0 : return NS_ERROR_NOT_AVAILABLE;
376 : }
377 :
378 0 : DBusGConnection* dbusGConnection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
379 :
380 0 : if (!dbusGConnection) {
381 0 : if (error) {
382 0 : g_printerr("Failed to open connection to session bus: %s\n", error->message);
383 0 : g_error_free(error);
384 : }
385 0 : return NS_ERROR_FAILURE;
386 : }
387 :
388 0 : char *uri = g_filename_to_uri(PromiseFlatCString(aPath).get(), nullptr, nullptr);
389 0 : if (uri == nullptr) {
390 0 : return NS_ERROR_FAILURE;
391 : }
392 :
393 0 : DBusConnection* dbusConnection = dbus_g_connection_get_connection(dbusGConnection);
394 : // Make sure we do not exit the entire program if DBus connection get lost.
395 0 : dbus_connection_set_exit_on_disconnect(dbusConnection, false);
396 :
397 : DBusGProxy* dbusGProxy = dbus_g_proxy_new_for_name(dbusGConnection,
398 : "org.freedesktop.FileManager1",
399 : "/org/freedesktop/FileManager1",
400 0 : "org.freedesktop.FileManager1");
401 :
402 0 : const char *uris[2] = { uri, nullptr };
403 0 : gboolean rv_dbus_call = dbus_g_proxy_call (dbusGProxy, "ShowItems", nullptr, G_TYPE_STRV, uris,
404 0 : G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID);
405 :
406 0 : g_object_unref(dbusGProxy);
407 0 : dbus_g_connection_unref(dbusGConnection);
408 0 : g_free(uri);
409 :
410 0 : if (!rv_dbus_call) {
411 0 : org_freedesktop_FileManager1_exists = false;
412 0 : return NS_ERROR_NOT_AVAILABLE;
413 : }
414 :
415 0 : return NS_OK;
416 : #endif
417 : }
418 :
419 : /**
420 : * Create or find already existing application info for specified command
421 : * and application name.
422 : * @param cmd command to execute
423 : * @param appName application name
424 : * @param appInfo location where created GAppInfo is stored
425 : * @return NS_OK when object is created, NS_ERROR_FAILURE otherwise.
426 : */
427 : NS_IMETHODIMP
428 0 : nsGIOService::CreateAppFromCommand(nsACString const& cmd,
429 : nsACString const& appName,
430 : nsIGIOMimeApp** appInfo)
431 : {
432 0 : GError *error = nullptr;
433 0 : *appInfo = nullptr;
434 :
435 0 : GAppInfo *app_info = nullptr, *app_info_from_list = nullptr;
436 0 : GList *apps = g_app_info_get_all();
437 0 : GList *apps_p = apps;
438 :
439 : // Try to find relevant and existing GAppInfo in all installed application
440 : // We do this by comparing each GAppInfo's executable with out own
441 0 : while (apps_p) {
442 0 : app_info_from_list = (GAppInfo*) apps_p->data;
443 0 : if (!app_info) {
444 : // If the executable is not absolute, get it's full path
445 0 : char *executable = g_find_program_in_path(g_app_info_get_executable(app_info_from_list));
446 :
447 0 : if (executable && strcmp(executable, PromiseFlatCString(cmd).get()) == 0) {
448 0 : g_object_ref (app_info_from_list);
449 0 : app_info = app_info_from_list;
450 : }
451 0 : g_free(executable);
452 : }
453 :
454 0 : g_object_unref(app_info_from_list);
455 0 : apps_p = apps_p->next;
456 : }
457 0 : g_list_free(apps);
458 :
459 0 : if (!app_info) {
460 0 : app_info = g_app_info_create_from_commandline(PromiseFlatCString(cmd).get(),
461 0 : PromiseFlatCString(appName).get(),
462 : G_APP_INFO_CREATE_SUPPORTS_URIS,
463 0 : &error);
464 : }
465 :
466 0 : if (!app_info) {
467 0 : g_warning("Cannot create application info from command: %s", error->message);
468 0 : g_error_free(error);
469 0 : return NS_ERROR_FAILURE;
470 : }
471 0 : nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info);
472 0 : NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY);
473 0 : NS_ADDREF(*appInfo = mozApp);
474 0 : return NS_OK;
475 : }
|