/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:expandtab:shiftwidth=2:tabstop=2: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsDBusRemoteServer.h" #include "nsCOMPtr.h" #include "mozilla/XREAppData.h" #include "mozilla/Base64.h" #include "mozilla/ScopeExit.h" #include "mozilla/GUniquePtr.h" #include "MainThreadUtils.h" #include "nsPrintfCString.h" #include "nsGTKToolkit.h" #include using namespace mozilla; // Mozilla has old GIO version in build roots #define G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE GBusNameOwnerFlags(1 << 2) static const char* introspect_template = "\n" "\n" " \n" " \n" " \n" " \n" " \n" "\n"; bool nsDBusRemoteServer::HandleOpenURL(const gchar* aInterfaceName, const gchar* aMethodName, const gchar* aParam) { nsPrintfCString ourInterfaceName("org.mozilla.%s", mAppName.get()); if ((strcmp("OpenURL", aMethodName) != 0) || (strcmp(ourInterfaceName.get(), aInterfaceName) != 0)) { g_warning("nsDBusRemoteServer: HandleOpenURL() called with wrong params!"); return false; } guint32 timestamp = gtk_get_current_event_time(); if (timestamp == GDK_CURRENT_TIME) { timestamp = guint32(g_get_monotonic_time() / 1000); } HandleCommandLine(aParam, timestamp); return true; } static void HandleMethodCall(GDBusConnection* aConnection, const gchar* aSender, const gchar* aObjectPath, const gchar* aInterfaceName, const gchar* aMethodName, GVariant* aParameters, GDBusMethodInvocation* aInvocation, gpointer aUserData) { MOZ_ASSERT(aUserData); MOZ_ASSERT(NS_IsMainThread()); if (!g_variant_is_of_type(aParameters, G_VARIANT_TYPE_TUPLE) || g_variant_n_children(aParameters) != 1) { g_warning( "nsDBusRemoteServer: HandleMethodCall: aParameters is not a tuple!"); g_dbus_method_invocation_return_error( aInvocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Method %s.%s.%s has wrong params!", aObjectPath, aInterfaceName, aMethodName); return; } gsize len; const auto* commandLine = (const char*)g_variant_get_fixed_array( g_variant_get_child_value(aParameters, 0), &len, sizeof(char)); if (!commandLine || !len) { g_warning( "nsDBusRemoteServer: HandleMethodCall: failed to get url string!"); g_dbus_method_invocation_return_error( aInvocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Method %s.%s.%s has wrong params!", aObjectPath, aInterfaceName, aMethodName); return; } int ret = static_cast(aUserData)->HandleOpenURL( aInterfaceName, aMethodName, commandLine); if (!ret) { g_dbus_method_invocation_return_error( aInvocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Method %s.%s.%s doesn't match OpenURL()", aObjectPath, aInterfaceName, aMethodName); return; } g_dbus_method_invocation_return_value(aInvocation, nullptr); } static GVariant* HandleGetProperty(GDBusConnection* aConnection, const gchar* aSender, const gchar* aObjectPath, const gchar* aInterfaceName, const gchar* aPropertyName, GError** aError, gpointer aUserData) { MOZ_ASSERT(aUserData); MOZ_ASSERT(NS_IsMainThread()); g_set_error(aError, G_IO_ERROR, G_IO_ERROR_FAILED, "%s:%s setting is not supported", aInterfaceName, aPropertyName); return nullptr; } static gboolean HandleSetProperty(GDBusConnection* aConnection, const gchar* aSender, const gchar* aObjectPath, const gchar* aInterfaceName, const gchar* aPropertyName, GVariant* aValue, GError** aError, gpointer aUserData) { MOZ_ASSERT(aUserData); MOZ_ASSERT(NS_IsMainThread()); g_set_error(aError, G_IO_ERROR, G_IO_ERROR_FAILED, "%s:%s setting is not supported", aInterfaceName, aPropertyName); return false; } static const GDBusInterfaceVTable gInterfaceVTable = { HandleMethodCall, HandleGetProperty, HandleSetProperty}; void nsDBusRemoteServer::OnBusAcquired(GDBusConnection* aConnection) { mPathName = nsPrintfCString("/org/mozilla/%s/Remote", mAppName.get()); static auto sDBusValidatePathName = (bool (*)(const char*, DBusError*))dlsym( RTLD_DEFAULT, "dbus_validate_path"); if (!sDBusValidatePathName || !sDBusValidatePathName(mPathName.get(), nullptr)) { g_warning("nsDBusRemoteServer: dbus_validate_path() failed!"); return; } GUniquePtr error; mIntrospectionData = dont_AddRef(g_dbus_node_info_new_for_xml( nsPrintfCString(introspect_template, mAppName.get()).get(), getter_Transfers(error))); if (!mIntrospectionData) { g_warning("nsDBusRemoteServer: g_dbus_node_info_new_for_xml() failed! %s", error->message); return; } mRegistrationId = g_dbus_connection_register_object( aConnection, mPathName.get(), mIntrospectionData->interfaces[0], &gInterfaceVTable, this, /* user_data */ nullptr, /* user_data_free_func */ getter_Transfers(error)); /* GError** */ if (mRegistrationId == 0) { g_warning( "nsDBusRemoteServer: g_dbus_connection_register_object() failed! %s", error->message); return; } } void nsDBusRemoteServer::OnNameAcquired(GDBusConnection* aConnection) { mConnection = aConnection; } void nsDBusRemoteServer::OnNameLost(GDBusConnection* aConnection) { mConnection = nullptr; if (!mRegistrationId) { return; } if (g_dbus_connection_unregister_object(aConnection, mRegistrationId)) { mRegistrationId = 0; } else { // Note: Most code examples in the internet probably dont't even check the // result here, but // according to the spec it _can_ return false. g_warning( "nsDBusRemoteServer: Unable to unregister root object from within " "onNameLost!"); } } nsresult nsDBusRemoteServer::Startup(const char* aAppName, const char* aProfileName) { MOZ_DIAGNOSTIC_ASSERT(!mDBusID); // Don't even try to start without any application/profile name if (!aAppName || aAppName[0] == '\0' || !aProfileName || aProfileName[0] == '\0') return NS_ERROR_INVALID_ARG; mAppName = aAppName; mozilla::XREAppData::SanitizeNameForDBus(mAppName); nsAutoCString profileName; MOZ_TRY( mozilla::Base64Encode(aProfileName, strlen(aProfileName), profileName)); mozilla::XREAppData::SanitizeNameForDBus(profileName); nsPrintfCString busName("org.mozilla.%s.%s", mAppName.get(), profileName.get()); if (busName.Length() > DBUS_MAXIMUM_NAME_LENGTH) { busName.Truncate(DBUS_MAXIMUM_NAME_LENGTH); } static auto sDBusValidateBusName = (bool (*)(const char*, DBusError*))dlsym( RTLD_DEFAULT, "dbus_validate_bus_name"); if (!sDBusValidateBusName) { g_warning("nsDBusRemoteServer: dbus_validate_bus_name() is missing!"); return NS_ERROR_FAILURE; } // We don't have a valid busName yet - try to create a default one. if (!sDBusValidateBusName(busName.get(), nullptr)) { busName = nsPrintfCString("org.mozilla.%s.%s", mAppName.get(), "default"); if (!sDBusValidateBusName(busName.get(), nullptr)) { // We failed completelly to get a valid bus name - just quit // to prevent crash at dbus_bus_request_name(). g_warning("nsDBusRemoteServer: dbus_validate_bus_name() failed!"); return NS_ERROR_FAILURE; } } mDBusID = g_bus_own_name( G_BUS_TYPE_SESSION, busName.get(), G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE, [](GDBusConnection* aConnection, const gchar*, gpointer aUserData) -> void { static_cast(aUserData)->OnBusAcquired(aConnection); }, [](GDBusConnection* aConnection, const gchar*, gpointer aUserData) -> void { static_cast(aUserData)->OnNameAcquired( aConnection); }, [](GDBusConnection* aConnection, const gchar*, gpointer aUserData) -> void { static_cast(aUserData)->OnNameLost(aConnection); }, this, nullptr); if (!mDBusID) { g_warning("nsDBusRemoteServer: g_bus_own_name() failed!"); return NS_ERROR_FAILURE; } return NS_OK; } void nsDBusRemoteServer::Shutdown() { OnNameLost(mConnection); if (mDBusID) { g_bus_unown_name(mDBusID); } mIntrospectionData = nullptr; }