Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/remote/nsDBusRemoteService.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:expandtab:shiftwidth=2:tabstop=2:
3
 */
4
/* This Source Code Form is subject to the terms of the Mozilla Public
5
 * License, v. 2.0. If a copy of the MPL was not distributed with this
6
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8
#include "nsDBusRemoteService.h"
9
#include "nsRemoteService.h"
10
11
#include "nsIBaseWindow.h"
12
#include "nsIDocShell.h"
13
#include "nsPIDOMWindow.h"
14
#include "mozilla/ModuleUtils.h"
15
#include "mozilla/Base64.h"
16
#include "nsIServiceManager.h"
17
#include "nsIWeakReference.h"
18
#include "nsIWidget.h"
19
#include "nsIAppShellService.h"
20
#include "nsAppShellCID.h"
21
#include "nsPrintfCString.h"
22
23
#include "nsCOMPtr.h"
24
25
#include "nsGTKToolkit.h"
26
27
#include <dbus/dbus.h>
28
#include <dbus/dbus-glib-lowlevel.h>
29
30
#include <dlfcn.h>
31
32
NS_IMPL_ISUPPORTS(nsDBusRemoteService,
33
                  nsIRemoteService)
34
35
NS_IMETHODIMP
36
nsDBusRemoteService::RegisterWindow(mozIDOMWindow* aWindow)
37
0
{
38
0
  // We don't listen for property change events on DBus remote
39
0
  return NS_ERROR_NOT_IMPLEMENTED;
40
0
}
41
42
const char* introspect_template =
43
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
44
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\";>\n"
45
"<node>\n"
46
" <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
47
"   <method name=\"Introspect\">\n"
48
"     <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
49
"   </method>\n"
50
" </interface>\n"
51
" <interface name=\"org.mozilla.%s\">\n"
52
"   <method name=\"OpenURL\">\n"
53
"     <arg name=\"url\" direction=\"in\" type=\"s\"/>\n"
54
"   </method>\n"
55
" </interface>\n"
56
"</node>\n";
57
58
DBusHandlerResult
59
nsDBusRemoteService::Introspect(DBusMessage *msg)
60
0
{
61
0
  DBusMessage *reply;
62
0
63
0
  reply = dbus_message_new_method_return(msg);
64
0
  if (!reply)
65
0
   return DBUS_HANDLER_RESULT_NEED_MEMORY;
66
0
67
0
  nsAutoCString introspect_xml;
68
0
  introspect_xml = nsPrintfCString(introspect_template, mAppName.get());
69
0
70
0
  const char *message = introspect_xml.get();
71
0
  dbus_message_append_args(reply,
72
0
     DBUS_TYPE_STRING, &message,
73
0
     DBUS_TYPE_INVALID);
74
0
75
0
  dbus_connection_send(mConnection, reply, nullptr);
76
0
  dbus_message_unref(reply);
77
0
78
0
  return DBUS_HANDLER_RESULT_HANDLED;
79
0
}
80
81
DBusHandlerResult
82
nsDBusRemoteService::OpenURL(DBusMessage *msg)
83
0
{
84
0
  DBusMessage *reply = nullptr;
85
0
  const char  *commandLine;
86
0
  int          length;
87
0
88
0
  if (!dbus_message_get_args(msg, nullptr, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
89
0
      &commandLine, &length, DBUS_TYPE_INVALID) || length == 0) {
90
0
    nsAutoCString errorMsg;
91
0
    errorMsg = nsPrintfCString("org.mozilla.%s.Error", mAppName.get());
92
0
    reply = dbus_message_new_error(msg, errorMsg.get(), "Wrong argument");
93
0
  } else {
94
0
    guint32 timestamp = gtk_get_current_event_time();
95
0
    if (timestamp == GDK_CURRENT_TIME) {
96
0
        timestamp = guint32(g_get_monotonic_time() / 1000);
97
0
    }
98
0
    nsRemoteService::HandleCommandLine(commandLine, nullptr, timestamp);
99
0
    reply = dbus_message_new_method_return(msg);
100
0
  }
101
0
102
0
  dbus_connection_send(mConnection, reply, nullptr);
103
0
  dbus_message_unref(reply);
104
0
105
0
  return DBUS_HANDLER_RESULT_HANDLED;
106
0
}
107
108
DBusHandlerResult
109
nsDBusRemoteService::HandleDBusMessage(DBusConnection *aConnection, DBusMessage *msg)
110
0
{
111
0
  NS_ASSERTION(mConnection == aConnection, "Wrong D-Bus connection.");
112
0
113
0
  const char *method = dbus_message_get_member(msg);
114
0
  const char *iface = dbus_message_get_interface(msg);
115
0
116
0
  if ((strcmp("Introspect", method) == 0) &&
117
0
    (strcmp("org.freedesktop.DBus.Introspectable", iface) == 0)) {
118
0
    return Introspect(msg);
119
0
  }
120
0
121
0
  nsAutoCString ourInterfaceName;
122
0
  ourInterfaceName = nsPrintfCString("org.mozilla.%s", mAppName.get());
123
0
124
0
  if ((strcmp("OpenURL", method) == 0) &&
125
0
     (strcmp(ourInterfaceName.get(), iface) == 0)) {
126
0
    return OpenURL(msg);
127
0
  }
128
0
129
0
  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
130
0
}
131
132
void
133
nsDBusRemoteService::UnregisterDBusInterface(DBusConnection *aConnection)
134
0
{
135
0
  NS_ASSERTION(mConnection == aConnection, "Wrong D-Bus connection.");
136
0
  // Not implemented
137
0
}
138
139
static DBusHandlerResult
140
message_handler(DBusConnection *conn, DBusMessage *msg, void *user_data)
141
0
{
142
0
  auto interface = static_cast<nsDBusRemoteService*>(user_data);
143
0
  return interface->HandleDBusMessage(conn, msg);
144
0
}
145
146
static void
147
unregister(DBusConnection *conn, void *user_data)
148
0
{
149
0
  auto interface = static_cast<nsDBusRemoteService*>(user_data);
150
0
  interface->UnregisterDBusInterface(conn);
151
0
}
152
153
static DBusObjectPathVTable remoteHandlersTable = {
154
  .unregister_function  = unregister,
155
  .message_function = message_handler,
156
};
157
158
NS_IMETHODIMP
159
nsDBusRemoteService::Startup(const char* aAppName, const char* aProfileName)
160
0
{
161
0
  if (mConnection && dbus_connection_get_is_connected(mConnection)) {
162
0
    // We're already connected so we don't need to reconnect
163
0
    return NS_ERROR_ALREADY_INITIALIZED;
164
0
  }
165
0
166
0
  // Don't even try to start without any application/profile name
167
0
  if (!aAppName || aAppName[0] == '\0' ||
168
0
      !aProfileName || aProfileName[0] == '\0')
169
0
    return NS_ERROR_INVALID_ARG;
170
0
171
0
  mConnection = already_AddRefed<DBusConnection>(
172
0
    dbus_bus_get(DBUS_BUS_SESSION, nullptr));
173
0
  if (!mConnection) {
174
0
    return NS_ERROR_FAILURE;
175
0
  }
176
0
  dbus_connection_set_exit_on_disconnect(mConnection, false);
177
0
178
0
  mAppName = aAppName;
179
0
  ToLowerCase(mAppName);
180
0
181
0
  // D-Bus names can contain only [a-z][A-Z][0-9]_
182
0
  // characters so adjust the profile string properly.
183
0
  nsAutoCString profileName;
184
0
  nsresult rv = mozilla::Base64Encode(nsAutoCString(aProfileName), profileName);
185
0
  NS_ENSURE_SUCCESS(rv, rv);
186
0
  profileName.ReplaceChar("+/=", '_');
187
0
188
0
  nsAutoCString busName;
189
0
  busName = nsPrintfCString("org.mozilla.%s.%s", mAppName.get(),
190
0
                                                 profileName.get());
191
0
  if (busName.Length() > DBUS_MAXIMUM_NAME_LENGTH)
192
0
    busName.Truncate(DBUS_MAXIMUM_NAME_LENGTH);
193
0
194
0
  static auto sDBusValidateBusName =
195
0
    (bool (*)(const char *, DBusError *))
196
0
    dlsym(RTLD_DEFAULT, "dbus_validate_bus_name");
197
0
  if (!sDBusValidateBusName) {
198
0
    return NS_ERROR_FAILURE;
199
0
  }
200
0
201
0
  // We don't have a valid busName yet - try to create a default one.
202
0
  if (!sDBusValidateBusName(busName.get(), nullptr)) {
203
0
    busName = nsPrintfCString("org.mozilla.%s.%s", mAppName.get(), "default");
204
0
    if (!sDBusValidateBusName(busName.get(), nullptr)) {
205
0
      // We failed completelly to get a valid bus name - just quit
206
0
      // to prevent crash at dbus_bus_request_name().
207
0
      return NS_ERROR_FAILURE;
208
0
    }
209
0
  }
210
0
211
0
  DBusError err;
212
0
  dbus_error_init(&err);
213
0
  dbus_bus_request_name(mConnection, busName.get(),
214
0
                       DBUS_NAME_FLAG_DO_NOT_QUEUE, &err);
215
0
  // The interface is already owned - there is another application/profile
216
0
  // instance already running.
217
0
  if (dbus_error_is_set(&err)) {
218
0
    dbus_error_free(&err);
219
0
    mConnection = nullptr;
220
0
    return NS_ERROR_FAILURE;
221
0
  }
222
0
223
0
  mPathName = nsPrintfCString("/org/mozilla/%s/Remote", mAppName.get());
224
0
  if (!dbus_connection_register_object_path(mConnection, mPathName.get(),
225
0
                                            &remoteHandlersTable, this)) {
226
0
    mConnection = nullptr;
227
0
    return NS_ERROR_FAILURE;
228
0
  }
229
0
230
0
  return NS_OK;
231
0
}
232
233
NS_IMETHODIMP
234
nsDBusRemoteService::Shutdown()
235
0
{
236
0
  dbus_connection_unregister_object_path(mConnection, mPathName.get());
237
0
238
0
  // dbus_connection_unref() will be called by RefPtr here.
239
0
  mConnection = nullptr;
240
0
  return NS_OK;
241
0
}