Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=8:
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/. */
8 #include "mozilla/ArrayUtils.h"
10 #include "nsXRemoteService.h"
11 #include "nsIObserverService.h"
12 #include "nsCOMPtr.h"
13 #include "nsIServiceManager.h"
14 #include "nsICommandLineRunner.h"
15 #include "nsICommandLine.h"
17 #include "nsIBaseWindow.h"
18 #include "nsIDocShell.h"
19 #include "nsIFile.h"
20 #include "nsIServiceManager.h"
21 #include "nsIWeakReference.h"
22 #include "nsIWidget.h"
23 #include "nsIAppShellService.h"
24 #include "nsAppShellCID.h"
25 #include "nsPIDOMWindow.h"
26 #include "mozilla/X11Util.h"
28 #include "nsCOMPtr.h"
29 #include "nsString.h"
30 #include "prprf.h"
31 #include "prenv.h"
32 #include "nsCRT.h"
34 #include "nsXULAppAPI.h"
36 #include <X11/Xlib.h>
37 #include <X11/Xatom.h>
39 using namespace mozilla;
41 #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION"
42 #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK"
43 #define MOZILLA_COMMAND_PROP "_MOZILLA_COMMAND"
44 #define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE"
45 #define MOZILLA_USER_PROP "_MOZILLA_USER"
46 #define MOZILLA_PROFILE_PROP "_MOZILLA_PROFILE"
47 #define MOZILLA_PROGRAM_PROP "_MOZILLA_PROGRAM"
48 #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
50 const unsigned char kRemoteVersion[] = "5.1";
52 #ifdef IS_BIG_ENDIAN
53 #define TO_LITTLE_ENDIAN32(x) \
54 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
55 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
56 #else
57 #define TO_LITTLE_ENDIAN32(x) (x)
58 #endif
60 // Minimize the roundtrips to the X server by getting all the atoms at once
61 static const char *XAtomNames[] = {
62 MOZILLA_VERSION_PROP,
63 MOZILLA_LOCK_PROP,
64 MOZILLA_COMMAND_PROP,
65 MOZILLA_RESPONSE_PROP,
66 MOZILLA_USER_PROP,
67 MOZILLA_PROFILE_PROP,
68 MOZILLA_PROGRAM_PROP,
69 MOZILLA_COMMANDLINE_PROP
70 };
71 static Atom XAtoms[MOZ_ARRAY_LENGTH(XAtomNames)];
73 Atom nsXRemoteService::sMozVersionAtom;
74 Atom nsXRemoteService::sMozLockAtom;
75 Atom nsXRemoteService::sMozCommandAtom;
76 Atom nsXRemoteService::sMozResponseAtom;
77 Atom nsXRemoteService::sMozUserAtom;
78 Atom nsXRemoteService::sMozProfileAtom;
79 Atom nsXRemoteService::sMozProgramAtom;
80 Atom nsXRemoteService::sMozCommandLineAtom;
82 nsXRemoteService * nsXRemoteService::sRemoteImplementation = 0;
85 static bool
86 FindExtensionParameterInCommand(const char* aParameterName,
87 const nsACString& aCommand,
88 char aSeparator,
89 nsACString* aValue)
90 {
91 nsAutoCString searchFor;
92 searchFor.Append(aSeparator);
93 searchFor.Append(aParameterName);
94 searchFor.Append('=');
96 nsACString::const_iterator start, end;
97 aCommand.BeginReading(start);
98 aCommand.EndReading(end);
99 if (!FindInReadable(searchFor, start, end))
100 return false;
102 nsACString::const_iterator charStart, charEnd;
103 charStart = end;
104 aCommand.EndReading(charEnd);
105 nsACString::const_iterator idStart = charStart, idEnd;
106 if (FindCharInReadable(aSeparator, charStart, charEnd)) {
107 idEnd = charStart;
108 } else {
109 idEnd = charEnd;
110 }
111 *aValue = nsDependentCSubstring(idStart, idEnd);
112 return true;
113 }
116 nsXRemoteService::nsXRemoteService()
117 {
118 }
120 void
121 nsXRemoteService::XRemoteBaseStartup(const char *aAppName, const char *aProfileName)
122 {
123 EnsureAtoms();
125 mAppName = aAppName;
126 ToLowerCase(mAppName);
128 mProfileName = aProfileName;
130 nsCOMPtr<nsIObserverService> obs(do_GetService("@mozilla.org/observer-service;1"));
131 if (obs) {
132 obs->AddObserver(this, "xpcom-shutdown", false);
133 obs->AddObserver(this, "quit-application", false);
134 }
135 }
137 void
138 nsXRemoteService::HandleCommandsFor(Window aWindowId)
139 {
140 // set our version
141 XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozVersionAtom, XA_STRING,
142 8, PropModeReplace, kRemoteVersion, sizeof(kRemoteVersion) - 1);
144 // get our username
145 unsigned char *logname;
146 logname = (unsigned char*) PR_GetEnv("LOGNAME");
147 if (logname) {
148 // set the property on the window if it's available
149 XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozUserAtom, XA_STRING,
150 8, PropModeReplace, logname, strlen((char*) logname));
151 }
153 XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozProgramAtom, XA_STRING,
154 8, PropModeReplace, (unsigned char*) mAppName.get(), mAppName.Length());
156 if (!mProfileName.IsEmpty()) {
157 XChangeProperty(mozilla::DefaultXDisplay(),
158 aWindowId, sMozProfileAtom, XA_STRING,
159 8, PropModeReplace,
160 (unsigned char*) mProfileName.get(), mProfileName.Length());
161 }
163 }
165 NS_IMETHODIMP
166 nsXRemoteService::Observe(nsISupports* aSubject,
167 const char *aTopic,
168 const char16_t *aData)
169 {
170 // This can be xpcom-shutdown or quit-application, but it's the same either
171 // way.
172 Shutdown();
173 return NS_OK;
174 }
176 bool
177 nsXRemoteService::HandleNewProperty(XID aWindowId, Display* aDisplay,
178 Time aEventTime,
179 Atom aChangedAtom,
180 nsIWeakReference* aDomWindow)
181 {
183 nsCOMPtr<nsIDOMWindow> window (do_QueryReferent(aDomWindow));
185 if (aChangedAtom == sMozCommandAtom || aChangedAtom == sMozCommandLineAtom) {
186 // We got a new command atom.
187 int result;
188 Atom actual_type;
189 int actual_format;
190 unsigned long nitems, bytes_after;
191 char *data = 0;
193 result = XGetWindowProperty (aDisplay,
194 aWindowId,
195 aChangedAtom,
196 0, /* long_offset */
197 (65536 / sizeof (long)), /* long_length */
198 True, /* atomic delete after */
199 XA_STRING, /* req_type */
200 &actual_type, /* actual_type return */
201 &actual_format, /* actual_format_return */
202 &nitems, /* nitems_return */
203 &bytes_after, /* bytes_after_return */
204 (unsigned char **)&data); /* prop_return
205 (we only care
206 about the first ) */
208 // Failed to get property off the window?
209 if (result != Success)
210 return false;
212 // Failed to get the data off the window or it was the wrong type?
213 if (!data || !TO_LITTLE_ENDIAN32(*reinterpret_cast<int32_t*>(data)))
214 return false;
216 // cool, we got the property data.
217 const char *response = nullptr;
218 if (aChangedAtom == sMozCommandAtom)
219 response = HandleCommand(data, window, aEventTime);
220 else if (aChangedAtom == sMozCommandLineAtom)
221 response = HandleCommandLine(data, window, aEventTime);
223 // put the property onto the window as the response
224 XChangeProperty (aDisplay, aWindowId,
225 sMozResponseAtom, XA_STRING,
226 8, PropModeReplace,
227 (const unsigned char *)response,
228 strlen (response));
229 XFree(data);
230 return true;
231 }
233 else if (aChangedAtom == sMozResponseAtom) {
234 // client accepted the response. party on wayne.
235 return true;
236 }
238 else if (aChangedAtom == sMozLockAtom) {
239 // someone locked the window
240 return true;
241 }
243 return false;
244 }
246 const char*
247 nsXRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow,
248 uint32_t aTimestamp)
249 {
250 nsresult rv;
252 nsCOMPtr<nsICommandLineRunner> cmdline
253 (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
254 if (NS_FAILED(rv))
255 return "509 internal error";
257 // 1) Make sure that it looks remotely valid with parens
258 // 2) Treat ping() immediately and specially
260 nsAutoCString command(aCommand);
261 int32_t p1, p2;
262 p1 = command.FindChar('(');
263 p2 = command.FindChar(')');
265 if (p1 == kNotFound || p2 == kNotFound || p1 == 0 || p2 < p1) {
266 return "500 command not parseable";
267 }
269 command.Truncate(p1);
270 command.Trim(" ", true, true);
271 ToLowerCase(command);
273 if (!command.EqualsLiteral("ping")) {
274 nsAutoCString desktopStartupID;
275 nsDependentCString cmd(aCommand);
276 FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
277 cmd, '\n',
278 &desktopStartupID);
280 const char* argv[3] = {"dummyappname", "-remote", aCommand};
281 rv = cmdline->Init(3, argv, nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT);
282 if (NS_FAILED(rv))
283 return "509 internal error";
285 if (aWindow)
286 cmdline->SetWindowContext(aWindow);
288 if (sRemoteImplementation)
289 sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
291 rv = cmdline->Run();
292 if (NS_ERROR_ABORT == rv)
293 return "500 command not parseable";
294 if (NS_FAILED(rv))
295 return "509 internal error";
296 }
298 return "200 executed command";
299 }
301 const char*
302 nsXRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
303 uint32_t aTimestamp)
304 {
305 nsresult rv;
307 nsCOMPtr<nsICommandLineRunner> cmdline
308 (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
309 if (NS_FAILED(rv))
310 return "509 internal error";
312 // the commandline property is constructed as an array of int32_t
313 // followed by a series of null-terminated strings:
314 //
315 // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
316 // (offset is from the beginning of the buffer)
318 int32_t argc = TO_LITTLE_ENDIAN32(*reinterpret_cast<int32_t*>(aBuffer));
319 char *wd = aBuffer + ((argc + 1) * sizeof(int32_t));
321 nsCOMPtr<nsIFile> lf;
322 rv = NS_NewNativeLocalFile(nsDependentCString(wd), true,
323 getter_AddRefs(lf));
324 if (NS_FAILED(rv))
325 return "509 internal error";
327 nsAutoCString desktopStartupID;
329 char **argv = (char**) malloc(sizeof(char*) * argc);
330 if (!argv) return "509 internal error";
332 int32_t *offset = reinterpret_cast<int32_t*>(aBuffer) + 1;
334 for (int i = 0; i < argc; ++i) {
335 argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]);
337 if (i == 0) {
338 nsDependentCString cmd(argv[0]);
339 FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
340 cmd, ' ',
341 &desktopStartupID);
342 }
343 }
345 rv = cmdline->Init(argc, argv, lf, nsICommandLine::STATE_REMOTE_AUTO);
347 free (argv);
348 if (NS_FAILED(rv)) {
349 return "509 internal error";
350 }
352 if (aWindow)
353 cmdline->SetWindowContext(aWindow);
355 if (sRemoteImplementation)
356 sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
358 rv = cmdline->Run();
360 if (NS_ERROR_ABORT == rv)
361 return "500 command not parseable";
363 if (NS_FAILED(rv))
364 return "509 internal error";
366 return "200 executed command";
367 }
369 void
370 nsXRemoteService::EnsureAtoms(void)
371 {
372 if (sMozVersionAtom)
373 return;
375 XInternAtoms(mozilla::DefaultXDisplay(), const_cast<char**>(XAtomNames),
376 ArrayLength(XAtomNames), False, XAtoms);
378 int i = 0;
379 sMozVersionAtom = XAtoms[i++];
380 sMozLockAtom = XAtoms[i++];
381 sMozCommandAtom = XAtoms[i++];
382 sMozResponseAtom = XAtoms[i++];
383 sMozUserAtom = XAtoms[i++];
384 sMozProfileAtom = XAtoms[i++];
385 sMozProgramAtom = XAtoms[i++];
386 sMozCommandLineAtom = XAtoms[i++];
387 }