263 lines
9.3 KiB
C++
263 lines
9.3 KiB
C++
/* -*- 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 "nsAppRunner.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 <dlfcn.h>
|
|
|
|
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 =
|
|
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection "
|
|
"1.0//EN\"\n"
|
|
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
|
|
"<node>\n"
|
|
" <interface name=\"org.mozilla.%s\">\n"
|
|
" <method name=\"OpenURL\">\n"
|
|
" <arg name=\"url\" direction=\"in\" type=\"ay\"/>\n"
|
|
" </method>\n"
|
|
" </interface>\n"
|
|
"</node>\n";
|
|
|
|
bool nsDBusRemoteServer::HandleOpenURL(const gchar* aInterfaceName,
|
|
const gchar* aMethodName,
|
|
Span<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;
|
|
}
|
|
|
|
HandleCommandLine(aParam, gtk_get_current_event_time());
|
|
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 = 0;
|
|
const char* commandLine = nullptr;
|
|
|
|
RefPtr<GVariant> variant =
|
|
dont_AddRef(g_variant_get_child_value(aParameters, 0));
|
|
if (variant) {
|
|
commandLine =
|
|
(const char*)g_variant_get_fixed_array(variant, &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<nsDBusRemoteServer*>(aUserData)->HandleOpenURL(
|
|
aInterfaceName, aMethodName, mozilla::Span(commandLine, len));
|
|
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<GError> 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 profile name
|
|
if (!aProfileName || aProfileName[0] == '\0') return NS_ERROR_INVALID_ARG;
|
|
|
|
// aAppName is remoting name which can be something like org.mozilla.appname
|
|
// or so.
|
|
// For DBus service we rather use general application DBus identifier
|
|
// which is shared by all DBus services.
|
|
gAppData->GetDBusAppName(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<nsDBusRemoteServer*>(aUserData)->OnBusAcquired(aConnection);
|
|
},
|
|
[](GDBusConnection* aConnection, const gchar*,
|
|
gpointer aUserData) -> void {
|
|
static_cast<nsDBusRemoteServer*>(aUserData)->OnNameAcquired(
|
|
aConnection);
|
|
},
|
|
[](GDBusConnection* aConnection, const gchar*,
|
|
gpointer aUserData) -> void {
|
|
static_cast<nsDBusRemoteServer*>(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;
|
|
}
|