diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/components/remote/nsDBusRemoteClient.cpp | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/toolkit/components/remote/nsDBusRemoteClient.cpp b/toolkit/components/remote/nsDBusRemoteClient.cpp new file mode 100644 index 0000000000..69e8d07c72 --- /dev/null +++ b/toolkit/components/remote/nsDBusRemoteClient.cpp @@ -0,0 +1,188 @@ +/* -*- 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); + dbus_connection_setup_with_g_main(mConnection, nullptr); + + 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; +} |