fune/toolkit/components/remote/nsDBusRemoteClient.cpp

187 lines
6 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=8:
*/
/* vim:set ts=8 sw=2 et cindent: */
/* 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 "nsDBusRemoteClient.h"
#include "RemoteUtils.h"
#include "mozilla/XREAppData.h"
#include "mozilla/Logging.h"
#include "mozilla/Base64.h"
#include "nsPrintfCString.h"
#include <dlfcn.h>
#include <dbus/dbus-glib-lowlevel.h>
#undef LOG
#ifdef MOZ_LOGGING
static mozilla::LazyLogModule sRemoteLm("nsDBusRemoteClient");
# define LOG(str, ...) \
MOZ_LOG(sRemoteLm, mozilla::LogLevel::Debug, (str, ##__VA_ARGS__))
#else
# define LOG(...)
#endif
nsDBusRemoteClient::nsDBusRemoteClient() {
mConnection = nullptr;
LOG("nsDBusRemoteClient::nsDBusRemoteClient");
}
nsDBusRemoteClient::~nsDBusRemoteClient() {
LOG("nsDBusRemoteClient::~nsDBusRemoteClient");
Shutdown();
}
nsresult nsDBusRemoteClient::Init() {
LOG("nsDBusRemoteClient::Init");
if (mConnection) return NS_OK;
mConnection =
already_AddRefed<DBusConnection>(dbus_bus_get(DBUS_BUS_SESSION, nullptr));
if (!mConnection) {
LOG(" failed to get DBus session");
return NS_ERROR_FAILURE;
}
dbus_connection_set_exit_on_disconnect(mConnection, false);
return NS_OK;
}
void nsDBusRemoteClient::Shutdown(void) {
LOG("nsDBusRemoteClient::Shutdown");
// This connection is owned by libdbus and we don't need to close it
mConnection = nullptr;
}
nsresult nsDBusRemoteClient::SendCommandLine(
const char* aProgram, const char* aProfile, int32_t argc, char** argv,
const char* aStartupToken, char** aResponse, bool* aWindowFound) {
NS_ENSURE_TRUE(aProgram, NS_ERROR_INVALID_ARG);
LOG("nsDBusRemoteClient::SendCommandLine");
int commandLineLength;
char* commandLine =
ConstructCommandLine(argc, argv, aStartupToken, &commandLineLength);
if (!commandLine) {
LOG(" failed to create command line");
return NS_ERROR_FAILURE;
}
nsresult rv =
DoSendDBusCommandLine(aProgram, aProfile, commandLine, commandLineLength);
free(commandLine);
*aWindowFound = NS_SUCCEEDED(rv);
LOG("DoSendDBusCommandLine %s", NS_SUCCEEDED(rv) ? "OK" : "FAILED");
return rv;
}
bool nsDBusRemoteClient::GetRemoteDestinationName(const char* aProgram,
const char* aProfile,
nsCString& aDestinationName) {
// We have a profile name - just create the destination.
// D-Bus names can contain only [a-z][A-Z][0-9]_
// characters so adjust the profile string properly.
nsAutoCString profileName;
nsresult rv = mozilla::Base64Encode(nsAutoCString(aProfile), profileName);
NS_ENSURE_SUCCESS(rv, false);
mozilla::XREAppData::SanitizeNameForDBus(profileName);
aDestinationName =
nsPrintfCString("org.mozilla.%s.%s", aProgram, profileName.get());
if (aDestinationName.Length() > DBUS_MAXIMUM_NAME_LENGTH)
aDestinationName.Truncate(DBUS_MAXIMUM_NAME_LENGTH);
static auto sDBusValidateBusName = (bool (*)(const char*, DBusError*))dlsym(
RTLD_DEFAULT, "dbus_validate_bus_name");
if (!sDBusValidateBusName) {
LOG(" failed to get dbus_validate_bus_name()");
return false;
}
if (!sDBusValidateBusName(aDestinationName.get(), nullptr)) {
// We don't have a valid busName yet - try to create a default one.
aDestinationName =
nsPrintfCString("org.mozilla.%s.%s", aProgram, "default");
if (!sDBusValidateBusName(aDestinationName.get(), nullptr)) {
// We failed completelly to get a valid bus name - just quit
// to prevent crash at dbus_bus_request_name().
LOG(" failed to validate profile DBus name");
return false;
}
}
return true;
}
nsresult nsDBusRemoteClient::DoSendDBusCommandLine(const char* aProgram,
const char* aProfile,
const char* aBuffer,
int aLength) {
LOG("nsDBusRemoteClient::DoSendDBusCommandLine()");
nsAutoCString appName(aProgram);
mozilla::XREAppData::SanitizeNameForDBus(appName);
nsAutoCString destinationName;
if (!GetRemoteDestinationName(appName.get(), aProfile, destinationName)) {
LOG(" failed to get remote destination name");
return NS_ERROR_FAILURE;
}
nsAutoCString pathName;
pathName = nsPrintfCString("/org/mozilla/%s/Remote", appName.get());
static auto sDBusValidatePathName = (bool (*)(const char*, DBusError*))dlsym(
RTLD_DEFAULT, "dbus_validate_path");
if (!sDBusValidatePathName ||
!sDBusValidatePathName(pathName.get(), nullptr)) {
LOG(" failed to validate path name");
return NS_ERROR_FAILURE;
}
nsAutoCString remoteInterfaceName;
remoteInterfaceName = nsPrintfCString("org.mozilla.%s", appName.get());
LOG(" DBus destination: %s\n", destinationName.get());
LOG(" DBus path: %s\n", pathName.get());
LOG(" DBus interface: %s\n", remoteInterfaceName.get());
RefPtr<DBusMessage> msg =
already_AddRefed<DBusMessage>(dbus_message_new_method_call(
destinationName.get(),
pathName.get(), // object to call on
remoteInterfaceName.get(), // interface to call on
"OpenURL")); // method name
if (!msg) {
LOG(" failed to create DBus message");
return NS_ERROR_FAILURE;
}
// append arguments
if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &aBuffer,
aLength, DBUS_TYPE_INVALID)) {
LOG(" failed to create DBus message");
return NS_ERROR_FAILURE;
}
// send message and get a handle for a reply
RefPtr<DBusMessage> reply = already_AddRefed<DBusMessage>(
dbus_connection_send_with_reply_and_block(mConnection, msg, -1, nullptr));
#ifdef MOZ_LOGGING
if (!reply) {
LOG(" failed to get DBus reply");
}
#endif
return reply ? NS_OK : NS_ERROR_FAILURE;
}