|
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/. */ |
|
7 |
|
8 #include "mozilla/ArrayUtils.h" |
|
9 |
|
10 #include "nsXRemoteService.h" |
|
11 #include "nsIObserverService.h" |
|
12 #include "nsCOMPtr.h" |
|
13 #include "nsIServiceManager.h" |
|
14 #include "nsICommandLineRunner.h" |
|
15 #include "nsICommandLine.h" |
|
16 |
|
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" |
|
27 |
|
28 #include "nsCOMPtr.h" |
|
29 #include "nsString.h" |
|
30 #include "prprf.h" |
|
31 #include "prenv.h" |
|
32 #include "nsCRT.h" |
|
33 |
|
34 #include "nsXULAppAPI.h" |
|
35 |
|
36 #include <X11/Xlib.h> |
|
37 #include <X11/Xatom.h> |
|
38 |
|
39 using namespace mozilla; |
|
40 |
|
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" |
|
49 |
|
50 const unsigned char kRemoteVersion[] = "5.1"; |
|
51 |
|
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 |
|
59 |
|
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)]; |
|
72 |
|
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; |
|
81 |
|
82 nsXRemoteService * nsXRemoteService::sRemoteImplementation = 0; |
|
83 |
|
84 |
|
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('='); |
|
95 |
|
96 nsACString::const_iterator start, end; |
|
97 aCommand.BeginReading(start); |
|
98 aCommand.EndReading(end); |
|
99 if (!FindInReadable(searchFor, start, end)) |
|
100 return false; |
|
101 |
|
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 } |
|
114 |
|
115 |
|
116 nsXRemoteService::nsXRemoteService() |
|
117 { |
|
118 } |
|
119 |
|
120 void |
|
121 nsXRemoteService::XRemoteBaseStartup(const char *aAppName, const char *aProfileName) |
|
122 { |
|
123 EnsureAtoms(); |
|
124 |
|
125 mAppName = aAppName; |
|
126 ToLowerCase(mAppName); |
|
127 |
|
128 mProfileName = aProfileName; |
|
129 |
|
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 } |
|
136 |
|
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); |
|
143 |
|
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 } |
|
152 |
|
153 XChangeProperty(mozilla::DefaultXDisplay(), aWindowId, sMozProgramAtom, XA_STRING, |
|
154 8, PropModeReplace, (unsigned char*) mAppName.get(), mAppName.Length()); |
|
155 |
|
156 if (!mProfileName.IsEmpty()) { |
|
157 XChangeProperty(mozilla::DefaultXDisplay(), |
|
158 aWindowId, sMozProfileAtom, XA_STRING, |
|
159 8, PropModeReplace, |
|
160 (unsigned char*) mProfileName.get(), mProfileName.Length()); |
|
161 } |
|
162 |
|
163 } |
|
164 |
|
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 } |
|
175 |
|
176 bool |
|
177 nsXRemoteService::HandleNewProperty(XID aWindowId, Display* aDisplay, |
|
178 Time aEventTime, |
|
179 Atom aChangedAtom, |
|
180 nsIWeakReference* aDomWindow) |
|
181 { |
|
182 |
|
183 nsCOMPtr<nsIDOMWindow> window (do_QueryReferent(aDomWindow)); |
|
184 |
|
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; |
|
192 |
|
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 ) */ |
|
207 |
|
208 // Failed to get property off the window? |
|
209 if (result != Success) |
|
210 return false; |
|
211 |
|
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; |
|
215 |
|
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); |
|
222 |
|
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 } |
|
232 |
|
233 else if (aChangedAtom == sMozResponseAtom) { |
|
234 // client accepted the response. party on wayne. |
|
235 return true; |
|
236 } |
|
237 |
|
238 else if (aChangedAtom == sMozLockAtom) { |
|
239 // someone locked the window |
|
240 return true; |
|
241 } |
|
242 |
|
243 return false; |
|
244 } |
|
245 |
|
246 const char* |
|
247 nsXRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow, |
|
248 uint32_t aTimestamp) |
|
249 { |
|
250 nsresult rv; |
|
251 |
|
252 nsCOMPtr<nsICommandLineRunner> cmdline |
|
253 (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv)); |
|
254 if (NS_FAILED(rv)) |
|
255 return "509 internal error"; |
|
256 |
|
257 // 1) Make sure that it looks remotely valid with parens |
|
258 // 2) Treat ping() immediately and specially |
|
259 |
|
260 nsAutoCString command(aCommand); |
|
261 int32_t p1, p2; |
|
262 p1 = command.FindChar('('); |
|
263 p2 = command.FindChar(')'); |
|
264 |
|
265 if (p1 == kNotFound || p2 == kNotFound || p1 == 0 || p2 < p1) { |
|
266 return "500 command not parseable"; |
|
267 } |
|
268 |
|
269 command.Truncate(p1); |
|
270 command.Trim(" ", true, true); |
|
271 ToLowerCase(command); |
|
272 |
|
273 if (!command.EqualsLiteral("ping")) { |
|
274 nsAutoCString desktopStartupID; |
|
275 nsDependentCString cmd(aCommand); |
|
276 FindExtensionParameterInCommand("DESKTOP_STARTUP_ID", |
|
277 cmd, '\n', |
|
278 &desktopStartupID); |
|
279 |
|
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"; |
|
284 |
|
285 if (aWindow) |
|
286 cmdline->SetWindowContext(aWindow); |
|
287 |
|
288 if (sRemoteImplementation) |
|
289 sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp); |
|
290 |
|
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 } |
|
297 |
|
298 return "200 executed command"; |
|
299 } |
|
300 |
|
301 const char* |
|
302 nsXRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow, |
|
303 uint32_t aTimestamp) |
|
304 { |
|
305 nsresult rv; |
|
306 |
|
307 nsCOMPtr<nsICommandLineRunner> cmdline |
|
308 (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv)); |
|
309 if (NS_FAILED(rv)) |
|
310 return "509 internal error"; |
|
311 |
|
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) |
|
317 |
|
318 int32_t argc = TO_LITTLE_ENDIAN32(*reinterpret_cast<int32_t*>(aBuffer)); |
|
319 char *wd = aBuffer + ((argc + 1) * sizeof(int32_t)); |
|
320 |
|
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"; |
|
326 |
|
327 nsAutoCString desktopStartupID; |
|
328 |
|
329 char **argv = (char**) malloc(sizeof(char*) * argc); |
|
330 if (!argv) return "509 internal error"; |
|
331 |
|
332 int32_t *offset = reinterpret_cast<int32_t*>(aBuffer) + 1; |
|
333 |
|
334 for (int i = 0; i < argc; ++i) { |
|
335 argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]); |
|
336 |
|
337 if (i == 0) { |
|
338 nsDependentCString cmd(argv[0]); |
|
339 FindExtensionParameterInCommand("DESKTOP_STARTUP_ID", |
|
340 cmd, ' ', |
|
341 &desktopStartupID); |
|
342 } |
|
343 } |
|
344 |
|
345 rv = cmdline->Init(argc, argv, lf, nsICommandLine::STATE_REMOTE_AUTO); |
|
346 |
|
347 free (argv); |
|
348 if (NS_FAILED(rv)) { |
|
349 return "509 internal error"; |
|
350 } |
|
351 |
|
352 if (aWindow) |
|
353 cmdline->SetWindowContext(aWindow); |
|
354 |
|
355 if (sRemoteImplementation) |
|
356 sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp); |
|
357 |
|
358 rv = cmdline->Run(); |
|
359 |
|
360 if (NS_ERROR_ABORT == rv) |
|
361 return "500 command not parseable"; |
|
362 |
|
363 if (NS_FAILED(rv)) |
|
364 return "509 internal error"; |
|
365 |
|
366 return "200 executed command"; |
|
367 } |
|
368 |
|
369 void |
|
370 nsXRemoteService::EnsureAtoms(void) |
|
371 { |
|
372 if (sMozVersionAtom) |
|
373 return; |
|
374 |
|
375 XInternAtoms(mozilla::DefaultXDisplay(), const_cast<char**>(XAtomNames), |
|
376 ArrayLength(XAtomNames), False, XAtoms); |
|
377 |
|
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 } |