/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 | } |